From eca6877a4f0f1e50ba8f58219465dd26d5621a65 Mon Sep 17 00:00:00 2001 From: Erika Date: Thu, 11 Sep 2025 13:55:58 +0200 Subject: [PATCH 01/10] chore: restructure services --- applications/tari_walletd/web_ui/src/App.tsx | 2 +- .../src/routes/AssetVault/AssetVault.tsx | 4 + .../src/routes/FlowEditor/FlowEditor.tsx | 10 +- .../web_ui/src/routes/Templates/Templates.tsx | 6 +- .../routes/Wallet/Components/AccessTokens.tsx | 2 +- .../WebauthnRegistration/Components/Login.tsx | 2 +- .../Components/Registration.tsx | 2 +- .../routes/WebauthnRegistration/Webauthn.tsx | 4 +- .../web_ui/src/services/api/helpers/types.ts | 27 ++ .../src/services/api/hooks/useAccounts.ts | 282 ++++++++++++++++++ .../web_ui/src/services/api/hooks/useAuth.tsx | 14 + .../web_ui/src/services/api/hooks/useKeys.tsx | 65 ++++ .../web_ui/src/services/api/hooks/useNfts.tsx | 69 +++++ .../src/services/api/hooks/useTemplate.tsx | 39 +++ .../api/hooks/useTemplatesAuthored.tsx | 19 ++ .../src/services/api/hooks/useTokens.tsx | 52 ++++ .../services/api/hooks/useTransactions.tsx | 78 +++++ .../src/services/api/hooks/useWalletInfo.ts | 31 ++ .../src/services/api/hooks/useWebauthn.tsx | 18 ++ .../web_ui/src/services/api/queryClient.ts | 34 +++ .../web_ui/src/services/store/accountStore.ts | 52 ++++ .../web_ui/src/services/store/authStore.ts | 33 ++ .../src/services/store/flowEditorStore.ts | 78 +++++ .../src/services/store/manifestStore.ts | 66 ++++ .../src/services/store/nftTransferStore.ts | 167 +++++++++++ .../web_ui/src/services/store/themeStore.ts | 43 +++ 26 files changed, 1185 insertions(+), 14 deletions(-) create mode 100644 applications/tari_walletd/web_ui/src/services/api/helpers/types.ts create mode 100644 applications/tari_walletd/web_ui/src/services/api/hooks/useAccounts.ts create mode 100644 applications/tari_walletd/web_ui/src/services/api/hooks/useAuth.tsx create mode 100644 applications/tari_walletd/web_ui/src/services/api/hooks/useKeys.tsx create mode 100644 applications/tari_walletd/web_ui/src/services/api/hooks/useNfts.tsx create mode 100644 applications/tari_walletd/web_ui/src/services/api/hooks/useTemplate.tsx create mode 100644 applications/tari_walletd/web_ui/src/services/api/hooks/useTemplatesAuthored.tsx create mode 100644 applications/tari_walletd/web_ui/src/services/api/hooks/useTokens.tsx create mode 100644 applications/tari_walletd/web_ui/src/services/api/hooks/useTransactions.tsx create mode 100644 applications/tari_walletd/web_ui/src/services/api/hooks/useWalletInfo.ts create mode 100644 applications/tari_walletd/web_ui/src/services/api/hooks/useWebauthn.tsx create mode 100644 applications/tari_walletd/web_ui/src/services/api/queryClient.ts create mode 100644 applications/tari_walletd/web_ui/src/services/store/accountStore.ts create mode 100644 applications/tari_walletd/web_ui/src/services/store/authStore.ts create mode 100644 applications/tari_walletd/web_ui/src/services/store/flowEditorStore.ts create mode 100644 applications/tari_walletd/web_ui/src/services/store/manifestStore.ts create mode 100644 applications/tari_walletd/web_ui/src/services/store/nftTransferStore.ts create mode 100644 applications/tari_walletd/web_ui/src/services/store/themeStore.ts diff --git a/applications/tari_walletd/web_ui/src/App.tsx b/applications/tari_walletd/web_ui/src/App.tsx index 002a54d16e..b3074da0cc 100644 --- a/applications/tari_walletd/web_ui/src/App.tsx +++ b/applications/tari_walletd/web_ui/src/App.tsx @@ -34,7 +34,7 @@ import AssetVault from "@routes/AssetVault/AssetVault"; import SettingsPage from "@routes/Settings/Settings"; import Auth, { AUTH_TOKEN_FOR_NONE_AUTH } from "@routes/Auth/Auth"; import Webauthn from "@routes/WebauthnRegistration/Webauthn"; -import useAuthStore from "@store/authStore"; +import useAuthStore from "./services/store/authStore"; import { useEffect } from "react"; import { useAuthMethod } from "@api/hooks/useAuth"; import AccessToken from "@routes/AccessToken/AccessToken"; diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/AssetVault.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/AssetVault.tsx index 960224f2d6..49ae6624c6 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/AssetVault.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/AssetVault.tsx @@ -27,6 +27,7 @@ import MyAssets from "./Components/MyAssets"; import { useEffect } from "react"; import FetchStatusCheck from "@components/FetchStatusCheck"; import useAuthStore from "@store/authStore"; +import { useWalletInfo } from "@api/hooks/useWalletInfo"; function AssetVault() { const account = useAccountStore((state) => state.account); @@ -34,6 +35,7 @@ function AssetVault() { const setPublicKey = useAccountStore((state) => state.setPublicKey); const { data: defaultAccount, isLoading, isError, error } = useAccountsGetDefault(); const authStore = useAuthStore(); + const { data: walletInfo } = useWalletInfo(); useEffect(() => { if (!isError && defaultAccount) { @@ -48,6 +50,8 @@ function AssetVault() { } }, [defaultAccount, isError]); + console.log("walletInfo", walletInfo); + return ( {account ? : } diff --git a/applications/tari_walletd/web_ui/src/routes/FlowEditor/FlowEditor.tsx b/applications/tari_walletd/web_ui/src/routes/FlowEditor/FlowEditor.tsx index 1f16c3f763..a9bcc1058d 100644 --- a/applications/tari_walletd/web_ui/src/routes/FlowEditor/FlowEditor.tsx +++ b/applications/tari_walletd/web_ui/src/routes/FlowEditor/FlowEditor.tsx @@ -25,7 +25,7 @@ import PageHeading from "@components/PageHeading"; import Grid from "@mui/material/Grid"; import { StyledPaper } from "@components/StyledComponents"; import { QueryBuilder, TemplateReader, useStore } from "@tari-project/tari-extension-query-builder"; -import useThemeStore from "../../store/themeStore"; +import useThemeStore from "@store/themeStore"; import { useCallback, useEffect, useRef } from "react"; import { Button, @@ -52,15 +52,15 @@ import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; import { useTheme } from "@mui/material/styles"; import Loading from "@components/Loading"; -import { useTemplateGet } from "../../api/hooks/useTemplate"; +import { useTemplateGet } from "@api/hooks/useTemplate"; import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; import FunctionsIcon from "@mui/icons-material/Functions"; import SettingsEthernetIcon from "@mui/icons-material/SettingsEthernet"; import { GeneratedCodeType, TariNetwork, TransactionProps } from "@tari-project/tari-extension-common"; -import { substateIdToString } from "../../utils/helpers"; +import { substateIdToString } from "@utils/helpers"; import CloseIcon from "@mui/icons-material/Close"; import { Highlight } from "prism-react-renderer"; -import useFlowEditorStore, { INITIAL_FLOW_STATE } from "../../store/flowEditorStore"; +import useFlowEditorStore, { INITIAL_FLOW_STATE } from "@store/flowEditorStore"; import { shortenString, UnsignedTransactionV1, @@ -70,7 +70,7 @@ import { TESTNET_XTR_FAUCET_ADDRESS, } from "@tari-project/typescript-bindings"; import { settingsGet, submitTransactionDryRun, transactionsSubmit, transactionsWaitResult } from "../../utils/json_rpc"; -import { useAccountsList } from "../../api/hooks/useAccounts"; +import { useAccountsList } from "@api/hooks/useAccounts"; import CopyAddress from "@components/CopyAddress"; const KNOWN_TEMPLATES = [ diff --git a/applications/tari_walletd/web_ui/src/routes/Templates/Templates.tsx b/applications/tari_walletd/web_ui/src/routes/Templates/Templates.tsx index 6061e9d622..45b8fd5c09 100644 --- a/applications/tari_walletd/web_ui/src/routes/Templates/Templates.tsx +++ b/applications/tari_walletd/web_ui/src/routes/Templates/Templates.tsx @@ -1,9 +1,9 @@ // Copyright 2025 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -import { useListTemplatesAuthored } from "../../api/hooks/useTemplatesAuthored"; +import { useListTemplatesAuthored } from "@api/hooks/useTemplatesAuthored"; import { useEffect, useState } from "react"; -import { useAccountsList } from "../../api/hooks/useAccounts"; +import { useAccountsList } from "@api/hooks/useAccounts"; import InputLabel from "@mui/material/InputLabel"; import Select, { SelectChangeEvent } from "@mui/material/Select/Select"; import { @@ -32,7 +32,7 @@ import { Collapse, TablePagination } from "@mui/material"; import { useTheme } from "@mui/material/styles"; import { SlCheck, SlClose } from "react-icons/sl"; import { handleChangePage, handleChangeRowsPerPage } from "../../utils/helpers"; -import useAccountStore from "../../store/accountStore"; +import useAccountStore from "@store/accountStore"; function getTypeAsString(funcType: FuncType): string { if (typeof funcType === "string") { diff --git a/applications/tari_walletd/web_ui/src/routes/Wallet/Components/AccessTokens.tsx b/applications/tari_walletd/web_ui/src/routes/Wallet/Components/AccessTokens.tsx index 7784e933aa..85cfa26856 100644 --- a/applications/tari_walletd/web_ui/src/routes/Wallet/Components/AccessTokens.tsx +++ b/applications/tari_walletd/web_ui/src/routes/Wallet/Components/AccessTokens.tsx @@ -46,7 +46,7 @@ import { useState } from "react"; import { IoCloseCircleOutline } from "react-icons/io5"; import FetchStatusCheck from "@components/FetchStatusCheck"; import { AccordionIconButton, CodeBlock, DataTableCell } from "@components/StyledComponents"; -import { useAuthRevokeToken, useGetAllTokens } from "../../../api/hooks/useTokens"; +import { useAuthRevokeToken, useGetAllTokens } from "@api/hooks/useTokens"; import type { Claims, JrpcPermission, JrpcPermissions } from "@tari-project/typescript-bindings"; import { jrpcPermissionToString } from "@tari-project/typescript-bindings"; import CopyAddress from "@components/CopyAddress"; diff --git a/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Login.tsx b/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Login.tsx index 650318ee58..8614211c52 100644 --- a/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Login.tsx +++ b/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Login.tsx @@ -12,7 +12,7 @@ import Button from "@mui/material/Button"; import { unauthenticated_client, webauthnStartAuth } from "../../../utils/json_rpc"; import { Buffer } from "buffer"; import { WebauthnFinishAuthRequest } from "@tari-project/typescript-bindings"; -import useAuthStore from "../../../store/authStore"; +import useAuthStore from "@store/authStore"; const getCredential = async (challenge: any, allowCredentials: any) => { const publicKeyCredentialRequestOptions: PublicKeyCredentialRequestOptions = { diff --git a/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Registration.tsx b/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Registration.tsx index 91e0c68161..3c053ac91e 100644 --- a/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Registration.tsx +++ b/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Components/Registration.tsx @@ -11,7 +11,7 @@ import { FormEvent, useState } from "react"; import { webauthnFinishRegistration, webauthnStartRegistration } from "../../../utils/json_rpc"; import { Buffer } from "buffer"; import Loading from "@components/Loading"; -import useAuthStore from "../../../store/authStore"; +import useAuthStore from "@store/authStore"; const WEBAUTHN_RP_ID = import.meta.env.VITE_DAEMON_WEBAUTHN_RP_ID || window.location.hostname; diff --git a/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Webauthn.tsx b/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Webauthn.tsx index 8baec93fb3..6d7c543217 100644 --- a/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Webauthn.tsx +++ b/applications/tari_walletd/web_ui/src/routes/WebauthnRegistration/Webauthn.tsx @@ -3,10 +3,10 @@ import { useEffect, useState } from "react"; import WebauthnLogin from "./Components/Login"; -import { useWebauthnAlreadyRegistered } from "../../api/hooks/useWebauthn"; +import { useWebauthnAlreadyRegistered } from "@api/hooks/useWebauthn"; import Loading from "@components/Loading"; import WebauthnRegistration from "./Components/Registration"; -import useAuthStore from "../../store/authStore"; +import useAuthStore from "@store/authStore"; import { useNavigate, useSearchParams } from "react-router-dom"; function Webauthn() { diff --git a/applications/tari_walletd/web_ui/src/services/api/helpers/types.ts b/applications/tari_walletd/web_ui/src/services/api/helpers/types.ts new file mode 100644 index 0000000000..ffd55f49ad --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/helpers/types.ts @@ -0,0 +1,27 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +export interface ApiError { + message: string; + description: string; + statusCode: string | number; +} diff --git a/applications/tari_walletd/web_ui/src/services/api/hooks/useAccounts.ts b/applications/tari_walletd/web_ui/src/services/api/hooks/useAccounts.ts new file mode 100644 index 0000000000..34526c6d46 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/hooks/useAccounts.ts @@ -0,0 +1,282 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { useMutation, useQuery } from "@tanstack/react-query"; +import { + accountsClaimBurn, + accountsConfidentialTransfer, + accountsCreate, + accountsCreateFreeTestCoins, + accountsGet, + accountsGetBalances, + accountsGetDefault, + accountsList, + accountsStealthTransfer, + accountsTransfer, + mintFaucetNfts, + nftList, + validatorsGetFees, +} from "@utils/json_rpc"; +import { ApiError } from "@api/helpers/types"; +import queryClient from "@api/queryClient"; +import type { + AccountOrKeyIndex, + ClaimBurnProof, + ClaimBurnRequest, + ComponentAddress, + ComponentAddressOrName, + ConfidentialTransferInputSelection, + ResourceType, +} from "@tari-project/typescript-bindings"; + +const DEFAULT_MAX_FEE = 2000; + +// Fees are passed as strings because Amount is tagged +export const useAccountsClaimBurn = () => { + return useMutation({ + mutationFn: (params: ClaimBurnRequest) => accountsClaimBurn(params), + onError: (error: ApiError) => { + error; + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ["accounts"] }); + }, + }); +}; + +export type AccountsCreateMutate = { + accountName?: string; + isDefault?: boolean; + keyId?: number | null; +}; + +export const useAccountsCreate = () => { + return useMutation({ + mutationFn: async (req: AccountsCreateMutate) => { + return await accountsCreate({ + account_name: req.accountName || "", + is_default: req.isDefault || null, + key_id: req.keyId || null, + }); + }, + onError: (error: ApiError) => { + error; + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ["accounts"] }); + }, + }); +}; + +export interface TransferParams { + account: ComponentAddress; + amount: number; + resource_address: string; + destination_public_key: string; + max_fee: number | null; + resourceType: ResourceType; + output_to_revealed: boolean; + input_selection: ConfidentialTransferInputSelection; + badge: string | null; + dry_run: boolean; +} + +export const useAccountsTransfer = () => { + return useMutation({ + mutationFn: (params: TransferParams) => { + const account = { ComponentAddress: params.account }; + const max_fee = params.max_fee || DEFAULT_MAX_FEE; + if (params.resourceType === "Confidential") { + let transferRequest = { + account, + amount: params.amount, + resource_address: params.resource_address, + destination_public_key: params.destination_public_key, + max_fee, + proof_from_badge_resource: params.badge, + input_selection: params.input_selection, + output_to_revealed: params.output_to_revealed, + dry_run: params.dry_run, + }; + return accountsConfidentialTransfer(transferRequest); + } else if (params.resourceType === "Stealth") { + let transferRequest = { + owner_account: account, + input_selection: params.input_selection, + resource_address: params.resource_address, + destination_public_key: params.destination_public_key, + max_fee, + blinded_output_amount: params.output_to_revealed ? 0 : params.amount, + revealed_output_amount: params.output_to_revealed ? params.amount : 0, + dry_run: params.dry_run, + }; + return accountsStealthTransfer(transferRequest); + } else { + // Fungible and NFTs + let transferRequest = { + account, + amount: params.amount, + resource_address: params.resource_address, + destination_public_key: params.destination_public_key, + max_fee, + proof_from_badge_resource: params.badge, + input_selection: params.input_selection, + output_to_revealed: params.output_to_revealed, + dry_run: params.dry_run, + }; + return accountsTransfer(transferRequest); + } + }, + onError: (error: ApiError) => { + error; + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ["accounts"] }); + }, + }); +}; + +export const useAccountsCreateFreeTestCoins = () => { + const createFreeTestCoins = async ({ + account, + amount, + fee, + }: { + account: ComponentAddressOrName; + amount: number; + fee: number | null; + }) => + accountsCreateFreeTestCoins({ + account, + amount, + max_fee: fee, + }); + + return useMutation({ + mutationFn: createFreeTestCoins, + onError: (error: ApiError) => { + console.error(error); + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ["transactions"] }); + queryClient.invalidateQueries({ queryKey: ["accounts_balances"] }); + }, + }); +}; + +export const useMintTestnetFaucetNfts = () => { + const callApi = async ({ + account, + numberToMint, + mutableData, + maxFee, + }: { + account: ComponentAddressOrName; + numberToMint: number; + mutableData: object; + maxFee: number | null; + }) => + mintFaucetNfts({ + account, + mutable_data: mutableData, + number_to_mint: BigInt(numberToMint), + max_fee: maxFee, + }); + + return useMutation({ + mutationFn: callApi, + onError: (error: ApiError) => { + console.error(error); + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ["transactions"] }); + queryClient.invalidateQueries({ queryKey: ["accounts_balances"] }); + queryClient.invalidateQueries({ queryKey: ["nfts_list"] }); + }, + }); +}; + +export const useAccountsList = (offset: number, limit: number, enabled: boolean = true) => { + return useQuery({ + queryKey: ["accounts"], + queryFn: () => accountsList({ offset, limit }), + enabled, + }); +}; + +export const useAccountsGetBalances = (account: ComponentAddress, refresh: boolean = false) => { + return useQuery({ + queryKey: [`accounts_balances_${account}`], + queryFn: () => accountsGetBalances({ account: { ComponentAddress: account }, refresh }), + refetchInterval: 5000, + structuralSharing: (oldData, newData) => { + if (!oldData || !newData) return newData; + if (JSON.stringify(oldData) === JSON.stringify(newData)) { + return oldData; + } + return newData; + }, + }); +}; + +export const refreshAccountsBalances = (account: ComponentAddress) => { + return useMutation({ + mutationFn: () => accountsGetBalances({ account: { ComponentAddress: account }, refresh: true }), + onError: (error: ApiError) => { + error; + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ["accounts_balances_" + account] }); + }, + }); +}; + +export const useAccountsGetDefault = () => { + return useQuery({ + queryKey: ["accounts_get_default"], + queryFn: () => accountsGetDefault({}), + refetchInterval: false, + notifyOnChangeProps: ["data", "error"], + retryOnMount: false, + retry: false, + }); +}; +export const useAccountsGet = (account: ComponentAddress) => { + return useQuery({ + queryKey: ["accounts_get_" + account], + queryFn: () => accountsGet({ name_or_address: { ComponentAddress: account } }), + }); +}; + +export const useAccountNFTsList = (account: ComponentAddress, offset: number, limit: number) => { + return useQuery({ + queryKey: ["nfts_list", account, offset, limit], + queryFn: () => nftList({ account: { ComponentAddress: account }, offset, limit }), + }); +}; + +export const useValidatorFees = (accountOrKeyIndex: AccountOrKeyIndex, shardGroup = null) => { + return useQuery({ + queryKey: ["validator_fees"], + queryFn: () => validatorsGetFees({ account_or_key: accountOrKeyIndex, shard_group: shardGroup }), + }); +}; diff --git a/applications/tari_walletd/web_ui/src/services/api/hooks/useAuth.tsx b/applications/tari_walletd/web_ui/src/services/api/hooks/useAuth.tsx new file mode 100644 index 0000000000..eea4c22325 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/hooks/useAuth.tsx @@ -0,0 +1,14 @@ +// Copyright 2025 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +import { useQuery } from "@tanstack/react-query"; +import { authGetMethod } from "@utils/json_rpc"; + +export const useAuthMethod = () => { + return useQuery({ + queryKey: ["auth_method"], + queryFn: () => { + return authGetMethod(); + }, + }); +}; diff --git a/applications/tari_walletd/web_ui/src/services/api/hooks/useKeys.tsx b/applications/tari_walletd/web_ui/src/services/api/hooks/useKeys.tsx new file mode 100644 index 0000000000..0791e373b5 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/hooks/useKeys.tsx @@ -0,0 +1,65 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { useQuery, useMutation } from "@tanstack/react-query"; +import { ApiError } from "@api/helpers/types"; +import queryClient from "@api/queryClient"; +import { keysCreate, keysList, keysSetActive } from "@utils/json_rpc"; +import { KeyBranch } from "@tari-project/typescript-bindings"; + +export const useKeysList = (branch: KeyBranch) => { + return useQuery({ + queryKey: ["keys_list", branch], + queryFn: () => { + return keysList({ branch }); + }, + }); +}; + +export const useKeysCreate = (branch: KeyBranch) => { + return useMutation({ + mutationFn: () => keysCreate({ branch, specific_index: null }), + onError: (error: ApiError) => { + error; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["keys_list"] }); + }, + }); +}; + +export const useKeysSetActive = () => { + const setActive = async (index: number) => { + const result = await keysSetActive({ index }); + return result; + }; + + return useMutation({ + mutationFn: setActive, + onError: (error: ApiError) => { + error; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["keys_list"] }); + }, + }); +}; diff --git a/applications/tari_walletd/web_ui/src/services/api/hooks/useNfts.tsx b/applications/tari_walletd/web_ui/src/services/api/hooks/useNfts.tsx new file mode 100644 index 0000000000..7e7d9f68a5 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/hooks/useNfts.tsx @@ -0,0 +1,69 @@ +// Copyright 2025 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +import { useMutation, useQuery } from "@tanstack/react-query"; +import { nftList, nftTransfer } from "@utils/json_rpc"; +import { ApiError } from "@api/helpers/types"; +import { TransferNftRequest } from "@tari-project/typescript-bindings"; +import queryClient from "@api/queryClient"; +import type { ComponentAddressOrName } from "@tari-project/typescript-bindings/dist"; + +export interface ListAccountNftsReq { + account: ComponentAddressOrName | null; + enabled?: boolean; +} + +export const useListNfts = (request: ListAccountNftsReq) => { + return useQuery({ + queryKey: ["list_nfts", request.account], + queryFn: async () => { + if (!request.account) { + return []; + } + const limit = 100; + let offset = 0; + let nfts = await nftList({ + account: request.account, + limit: limit, + offset: offset, + }); + let result = nfts.nfts; + while (nfts.nfts.length > 0) { + offset += limit; + nfts = await nftList({ + account: request.account, + limit: 1, + offset: offset, + }); + result = result.concat(nfts.nfts); + } + return result; + }, + enabled: request.enabled !== false && !!request.account, + retry: false, + }); +}; + +export const useNftsTransfer = (request: TransferNftRequest) => { + return useMutation({ + mutationFn: () => { + return nftTransfer(request); + }, + onError: (error: ApiError) => { + error; + }, + onSettled: () => { + // Invalidate all NFT-related queries + queryClient.invalidateQueries({ + predicate: (query) => { + const key = query.queryKey[0]; + return typeof key === "string" && ( + key === "nfts" || + key === "list_nfts" || + key.startsWith("nfts_list_") + ); + } + }); + }, + }); +}; diff --git a/applications/tari_walletd/web_ui/src/services/api/hooks/useTemplate.tsx b/applications/tari_walletd/web_ui/src/services/api/hooks/useTemplate.tsx new file mode 100644 index 0000000000..7904de8bc5 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/hooks/useTemplate.tsx @@ -0,0 +1,39 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { useQuery } from "@tanstack/react-query"; +import { templatesGet } from "@utils/json_rpc"; +import { TemplatesGetRequest } from "@tari-project/typescript-bindings"; + +export const useTemplateGet = (request: TemplatesGetRequest, options = {}) => { + return useQuery({ + queryKey: ["template_get", request], + queryFn: () => { + return templatesGet(request); + }, + refetchInterval: false, + notifyOnChangeProps: ["data", "error"], + retryOnMount: false, + retry: false, + ...options, + }); +}; diff --git a/applications/tari_walletd/web_ui/src/services/api/hooks/useTemplatesAuthored.tsx b/applications/tari_walletd/web_ui/src/services/api/hooks/useTemplatesAuthored.tsx new file mode 100644 index 0000000000..59b2f968e5 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/hooks/useTemplatesAuthored.tsx @@ -0,0 +1,19 @@ +// Copyright 2025 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +import { useQuery } from "@tanstack/react-query"; +import { templatesListAuthored } from "@utils/json_rpc"; +import { TemplatesListAuthoredRequest } from "@tari-project/typescript-bindings"; + +export const useListTemplatesAuthored = (request: TemplatesListAuthoredRequest) => { + return useQuery({ + queryKey: ["templates_list_authored", request], + queryFn: () => { + return templatesListAuthored(request); + }, + refetchInterval: false, + notifyOnChangeProps: ["data", "error"], + retryOnMount: false, + retry: false, + }); +}; diff --git a/applications/tari_walletd/web_ui/src/services/api/hooks/useTokens.tsx b/applications/tari_walletd/web_ui/src/services/api/hooks/useTokens.tsx new file mode 100644 index 0000000000..2e0077528f --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/hooks/useTokens.tsx @@ -0,0 +1,52 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { useQuery, useMutation } from "@tanstack/react-query"; +import { ApiError } from "@api/helpers/types"; +import queryClient from "@api/queryClient"; +import { authGetAllJwt, authRevoke } from "@utils/json_rpc"; + +export const useGetAllTokens = () => { + return useQuery({ + queryKey: ["jwts_list"], + queryFn: () => { + return authGetAllJwt({}); + }, + }); +}; + +export const useAuthRevokeToken = () => { + const revokeToken = async (token: number) => { + const result = await authRevoke({ permission_token_id: token }); + return result; + }; + return useMutation({ + mutationFn: revokeToken, + onError: (error: ApiError) => { + error; + console.error(error); + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ["jwts_list"] }); + }, + }); +}; diff --git a/applications/tari_walletd/web_ui/src/services/api/hooks/useTransactions.tsx b/applications/tari_walletd/web_ui/src/services/api/hooks/useTransactions.tsx new file mode 100644 index 0000000000..54728ec568 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/hooks/useTransactions.tsx @@ -0,0 +1,78 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { useMutation, useQuery } from "@tanstack/react-query"; +import { + transactionsGet, + transactionsGetAll, + transactionsPublishTemplate, + transactionsSubmitManifest, + transactionsWaitResult, + validatorsGetFees, +} from "@utils/json_rpc"; +import { ApiError } from "@api/helpers/types"; +import queryClient from "@api/queryClient"; + +import type { AccountOrKeyIndex, TransactionGetAllRequest, TransactionStatus } from "@tari-project/typescript-bindings"; + +export const useTransactionDetails = (hash: string) => { + return useQuery({ + queryKey: ["transaction_details"], + queryFn: () => { + return transactionsGet({ transaction_id: hash }); + }, + }); +}; + +export const useGetAllTransactions = (req: TransactionGetAllRequest) => { + return useQuery({ + queryKey: ["transactions", req.status], + queryFn: () => transactionsGetAll(req), + refetchInterval: 5000, + placeholderData: (previousData) => previousData, + }); +}; + +export const usePublishTemplate = () => { + return useMutation({ + mutationFn: transactionsPublishTemplate, + onError: (error: ApiError) => { + console.error(error); + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ["transactions"] }); + queryClient.invalidateQueries({ queryKey: ["accounts_balances"] }); + }, + }); +}; +export const useSubmitManifest = () => { + return useMutation({ + mutationFn: transactionsSubmitManifest, + onError: (error: ApiError) => { + console.error(error); + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ["transactions"] }); + queryClient.invalidateQueries({ queryKey: ["accounts_balances"] }); + }, + }); +}; diff --git a/applications/tari_walletd/web_ui/src/services/api/hooks/useWalletInfo.ts b/applications/tari_walletd/web_ui/src/services/api/hooks/useWalletInfo.ts new file mode 100644 index 0000000000..27a9befc3c --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/hooks/useWalletInfo.ts @@ -0,0 +1,31 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { useQuery } from "@tanstack/react-query"; +import { walletGetInfo } from "@utils/json_rpc"; + +export const useWalletInfo = () => { + return useQuery({ + queryKey: ["wallet_info"], + queryFn: () => walletGetInfo(), + }); +}; diff --git a/applications/tari_walletd/web_ui/src/services/api/hooks/useWebauthn.tsx b/applications/tari_walletd/web_ui/src/services/api/hooks/useWebauthn.tsx new file mode 100644 index 0000000000..bcb846fa2b --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/hooks/useWebauthn.tsx @@ -0,0 +1,18 @@ +// Copyright 2025 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +import { useQuery } from "@tanstack/react-query"; +import { webauthnAlreadyRegistered } from "@utils/json_rpc"; + +export const useWebauthnAlreadyRegistered = (username: string) => { + return useQuery({ + queryKey: ["webauthn_already_registered", username], + queryFn: () => { + return webauthnAlreadyRegistered(username); + }, + refetchInterval: false, + notifyOnChangeProps: ["data", "error"], + retryOnMount: false, + retry: false, + }); +}; diff --git a/applications/tari_walletd/web_ui/src/services/api/queryClient.ts b/applications/tari_walletd/web_ui/src/services/api/queryClient.ts new file mode 100644 index 0000000000..8ce308c767 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/api/queryClient.ts @@ -0,0 +1,34 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { QueryClient } from "@tanstack/react-query"; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: 3, + structuralSharing: true, + }, + }, +}); + +export default queryClient; diff --git a/applications/tari_walletd/web_ui/src/services/store/accountStore.ts b/applications/tari_walletd/web_ui/src/services/store/accountStore.ts new file mode 100644 index 0000000000..3b4f34534b --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/store/accountStore.ts @@ -0,0 +1,52 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { create } from "zustand"; +import { Account } from "@tari-project/typescript-bindings"; + +interface Store { + showBalance: boolean; + setShowBalance: (show: boolean) => void; + account: Account | null; + setAccount: (account: Account) => void; + publicKey: string; + setPublicKey: (publicKey: string) => void; + indexer: string; + setIndexer: (indexer: string) => void; + popup: any; + setPopup: (popup: any) => void; +} + +const useAccountStore = create()((set) => ({ + showBalance: true, + setShowBalance: (show) => set({ showBalance: show }), + account: null, + setAccount: (account) => set({ account: account }), + publicKey: "", + setPublicKey: (publicKey) => set({ publicKey: publicKey }), + indexer: "", + setIndexer: (indexer) => set({ indexer: indexer }), + popup: { visible: false }, + setPopup: (popup) => set({ popup: { visible: true, ...popup } }), +})); + +export default useAccountStore; diff --git a/applications/tari_walletd/web_ui/src/services/store/authStore.ts b/applications/tari_walletd/web_ui/src/services/store/authStore.ts new file mode 100644 index 0000000000..b25544deb5 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/store/authStore.ts @@ -0,0 +1,33 @@ +// Copyright 2025 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +import { create } from "zustand"; +import { persist } from "zustand/middleware"; +import { AUTH_TOKEN_FOR_NONE_AUTH } from "@routes/Auth/Auth"; + +interface Store { + username: string; + authToken: string; + setAuthToken: (token: string) => void; + clearToken: () => void; +} + +const useAuthStore = create()( + persist( + (set) => ({ + username: "tari-wallet-webui", + authToken: "", + setAuthToken: (token) => set({ authToken: token }), + clearToken: () => + set((s) => { + if (s.authToken === AUTH_TOKEN_FOR_NONE_AUTH) { + return {}; + } + return { authToken: "" }; + }), + }), + { name: "tari-auth" }, + ), +); + +export default useAuthStore; diff --git a/applications/tari_walletd/web_ui/src/services/store/flowEditorStore.ts b/applications/tari_walletd/web_ui/src/services/store/flowEditorStore.ts new file mode 100644 index 0000000000..b64965ed17 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/store/flowEditorStore.ts @@ -0,0 +1,78 @@ +// Copyright 2025. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { create } from "zustand"; +import { GeneratedCodeType } from "@tari-project/tari-extension-common"; +import { AccountInfo } from "@tari-project/typescript-bindings"; +import { persist } from "zustand/middleware"; + +interface FlowEditorStore { + panelOpen: boolean; + setPanelOpen: (open: boolean) => void; + templateId: string; + setTemplateId: (id: string) => void; + codeDialogOpen: boolean; + setCodeDialogOpen: (open: boolean) => void; + generatedCode: string; + setGeneratedCode: (code: string) => void; + generatedCodeType: GeneratedCodeType | null; + setGeneratedCodeType: (type: GeneratedCodeType | null) => void; + account: AccountInfo | undefined; + setAccount: (account: AccountInfo | undefined) => void; + setCurrentState: (state: any) => void; + currentState: any; + fee: number; + setFee: (fee: number) => void; +} + +export const INITIAL_FLOW_STATE = { + $schema: "https://tari-project.github.io/tari-vscode-nocode-extension/schemas/tari-schema-v1.0.json", + version: "1.0", + nodes: [], + edges: [], +}; + +const useFlowEditorStore = create()( + persist( + (set) => ({ + panelOpen: true, + setPanelOpen: (open) => set({ panelOpen: open }), + templateId: "", + setTemplateId: (id) => set({ templateId: id }), + codeDialogOpen: false, + setCodeDialogOpen: (open) => set({ codeDialogOpen: open }), + generatedCode: "", + setGeneratedCode: (code) => set({ generatedCode: code }), + generatedCodeType: null, + setGeneratedCodeType: (type) => set({ generatedCodeType: type }), + account: undefined, + setAccount: (account) => set({ account }), + currentState: INITIAL_FLOW_STATE, + setCurrentState: (json) => set({ currentState: json }), + fee: 3000, + setFee: (fee) => set({ fee }), + }), + { name: "flowEditor" }, + ), +); + +export default useFlowEditorStore; diff --git a/applications/tari_walletd/web_ui/src/services/store/manifestStore.ts b/applications/tari_walletd/web_ui/src/services/store/manifestStore.ts new file mode 100644 index 0000000000..efa7e103f0 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/store/manifestStore.ts @@ -0,0 +1,66 @@ +// Copyright 2025. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +const DEFAULT_CODE = ` +// use template_xxx as TemplateName; + +fn main() { + // TemplateName::call_something(); + // let account = var!["account"]; + // let bucket = account.withdraw(1000); +}`; + +interface Store { + code: string; + setCode: (code: string) => void; + variables: Record; + addVariable: (key: string, value: string) => void; + removeVariable: (key: string) => void; +} + +const useManifestCodeStore = create()( + persist( + (set) => ({ + code: DEFAULT_CODE, + setCode: (code: string) => set(() => ({ code })), + variables: {}, + addVariable: (key: string, value: string) => + set((state) => ({ + variables: { + ...state.variables, + [key]: value, + }, + })), + removeVariable: (key: string) => + set((state) => { + const { [key]: _, ...rest } = state.variables; + return { variables: rest }; + }), + }), + { name: "manifest-code" }, + ), +); + +export default useManifestCodeStore; diff --git a/applications/tari_walletd/web_ui/src/services/store/nftTransferStore.ts b/applications/tari_walletd/web_ui/src/services/store/nftTransferStore.ts new file mode 100644 index 0000000000..de30837760 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/store/nftTransferStore.ts @@ -0,0 +1,167 @@ +// Copyright 2025. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { create } from 'zustand'; +import type { NonFungibleId, ResourceAddress } from '@tari-project/typescript-bindings'; + +export type DialogStep = 'form' | 'confirmation' | 'result'; + +interface TransferFormState { + payerAccount: string; + nfts: NonFungibleId[]; + targetAccountPublicKey: string; + maxFee: string; + resourceAddress: ResourceAddress; +} + +interface TransferResult { + success: boolean; + message: string; +} + +interface Validity { + payerAccount: boolean; + nfts: boolean; + targetAccountPublicKey: boolean; +} + +interface NftTransferState { + // Dialog state + currentStep: DialogStep; + disabled: boolean; + + // Form state + transferFormState: TransferFormState; + validity: Validity; + + // Fee estimation state + estimatedFee: number | null; + isEstimatingFee: boolean; + + // Result state + transferResult: TransferResult | null; + + // Auto-close state + autoCloseTimeoutId: NodeJS.Timeout | null; + + // Actions + setCurrentStep: (step: DialogStep) => void; + setDisabled: (disabled: boolean) => void; + setTransferFormState: (state: Partial) => void; + setValidity: (validity: Partial) => void; + setEstimatedFee: (fee: number | null) => void; + setIsEstimatingFee: (estimating: boolean) => void; + setTransferResult: (result: TransferResult | null) => void; + setAutoCloseTimeoutId: (timeoutId: NodeJS.Timeout | null) => void; + + // Complex actions + updateFormValue: (name: string, value: string, isValid?: boolean) => void; + initializeFormState: (preSelectedNftId?: NonFungibleId, preSelectedResourceAddress?: ResourceAddress, accountAddress?: string) => void; + resetState: (preSelectedNftId?: NonFungibleId, preSelectedResourceAddress?: ResourceAddress) => void; + isFormValid: () => boolean; +} + +const createInitialFormState = ( + preSelectedNftId?: NonFungibleId, + preSelectedResourceAddress?: ResourceAddress, + accountAddress?: string +): TransferFormState => ({ + payerAccount: accountAddress || "", + nfts: preSelectedNftId ? [preSelectedNftId] : [], + targetAccountPublicKey: "", + maxFee: "", + resourceAddress: (preSelectedResourceAddress || "") as ResourceAddress, +}); + +const createInitialValidity = (preSelectedNftId?: NonFungibleId): Validity => ({ + payerAccount: true, + nfts: preSelectedNftId ? true : false, + targetAccountPublicKey: false, +}); + +export const useNftTransferStore = create((set, get) => ({ + // Initial state + currentStep: 'form', + disabled: false, + transferFormState: createInitialFormState(), + validity: createInitialValidity(), + estimatedFee: null, + isEstimatingFee: false, + transferResult: null, + autoCloseTimeoutId: null, + + // Simple setters + setCurrentStep: (step) => set({ currentStep: step }), + setDisabled: (disabled) => set({ disabled }), + setTransferFormState: (state) => set((prev) => ({ + transferFormState: { ...prev.transferFormState, ...state } + })), + setValidity: (validity) => set((prev) => ({ + validity: { ...prev.validity, ...validity } + })), + setEstimatedFee: (fee) => set({ estimatedFee: fee }), + setIsEstimatingFee: (estimating) => set({ isEstimatingFee: estimating }), + setTransferResult: (result) => set({ transferResult: result }), + setAutoCloseTimeoutId: (timeoutId) => set({ autoCloseTimeoutId: timeoutId }), + + // Complex actions + updateFormValue: (name, value, isValid) => { + const { transferFormState, validity } = get(); + + set({ + transferFormState: { ...transferFormState, [name]: value }, + validity: isValid !== undefined ? { ...validity, [name]: isValid } : validity + }); + }, + + initializeFormState: (preSelectedNftId, preSelectedResourceAddress, accountAddress) => { + set({ + transferFormState: createInitialFormState(preSelectedNftId, preSelectedResourceAddress, accountAddress), + validity: createInitialValidity(preSelectedNftId), + }); + }, + + resetState: (preSelectedNftId, preSelectedResourceAddress) => { + const { autoCloseTimeoutId } = get(); + + // Clear any active timeout + if (autoCloseTimeoutId) { + clearTimeout(autoCloseTimeoutId); + } + + set({ + currentStep: 'form', + disabled: false, + transferFormState: createInitialFormState(preSelectedNftId, preSelectedResourceAddress), + validity: createInitialValidity(preSelectedNftId), + estimatedFee: null, + isEstimatingFee: false, + transferResult: null, + autoCloseTimeoutId: null, + }); + }, + + isFormValid: () => { + const { validity } = get(); + return Object.values(validity).every((v) => v); + }, +})); \ No newline at end of file diff --git a/applications/tari_walletd/web_ui/src/services/store/themeStore.ts b/applications/tari_walletd/web_ui/src/services/store/themeStore.ts new file mode 100644 index 0000000000..ef780fcbdd --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/store/themeStore.ts @@ -0,0 +1,43 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +interface Store { + themeMode: "light" | "dark"; + setThemeMode: (mode: "light" | "dark") => void; +} + +const useThemeStore = create()( + persist( + (set) => ({ + themeMode: "light", + setThemeMode: (mode) => set({ themeMode: mode }), + }), + { + name: "tari-theme", + }, + ), +); + +export default useThemeStore; From 4e1504a83de51ea2dd74429af3a3a093465dd851 Mon Sep 17 00:00:00 2001 From: Erika Date: Thu, 11 Sep 2025 13:59:33 +0200 Subject: [PATCH 02/10] chore: restructure services --- .../web_ui/src/api/helpers/types.ts | 27 -- .../web_ui/src/api/hooks/useAccounts.ts | 282 ------------------ .../web_ui/src/api/hooks/useAuth.tsx | 14 - .../web_ui/src/api/hooks/useKeys.tsx | 65 ---- .../web_ui/src/api/hooks/useNfts.tsx | 69 ----- .../web_ui/src/api/hooks/useTemplate.tsx | 39 --- .../src/api/hooks/useTemplatesAuthored.tsx | 19 -- .../web_ui/src/api/hooks/useTokens.tsx | 52 ---- .../web_ui/src/api/hooks/useTransactions.tsx | 78 ----- .../web_ui/src/api/hooks/useWebauthn.tsx | 18 -- .../web_ui/src/api/queryClient.ts | 34 --- .../web_ui/src/store/accountStore.ts | 52 ---- .../web_ui/src/store/authStore.ts | 33 -- .../web_ui/src/store/flowEditorStore.ts | 78 ----- .../web_ui/src/store/manifestStore.ts | 66 ---- .../web_ui/src/store/nftTransferStore.ts | 167 ----------- .../web_ui/src/store/themeStore.ts | 43 --- .../tari_walletd/web_ui/tsconfig.json | 4 +- .../tari_walletd/web_ui/vite.config.ts | 4 +- 19 files changed, 4 insertions(+), 1140 deletions(-) delete mode 100644 applications/tari_walletd/web_ui/src/api/helpers/types.ts delete mode 100644 applications/tari_walletd/web_ui/src/api/hooks/useAccounts.ts delete mode 100644 applications/tari_walletd/web_ui/src/api/hooks/useAuth.tsx delete mode 100644 applications/tari_walletd/web_ui/src/api/hooks/useKeys.tsx delete mode 100644 applications/tari_walletd/web_ui/src/api/hooks/useNfts.tsx delete mode 100644 applications/tari_walletd/web_ui/src/api/hooks/useTemplate.tsx delete mode 100644 applications/tari_walletd/web_ui/src/api/hooks/useTemplatesAuthored.tsx delete mode 100644 applications/tari_walletd/web_ui/src/api/hooks/useTokens.tsx delete mode 100644 applications/tari_walletd/web_ui/src/api/hooks/useTransactions.tsx delete mode 100644 applications/tari_walletd/web_ui/src/api/hooks/useWebauthn.tsx delete mode 100644 applications/tari_walletd/web_ui/src/api/queryClient.ts delete mode 100644 applications/tari_walletd/web_ui/src/store/accountStore.ts delete mode 100644 applications/tari_walletd/web_ui/src/store/authStore.ts delete mode 100644 applications/tari_walletd/web_ui/src/store/flowEditorStore.ts delete mode 100644 applications/tari_walletd/web_ui/src/store/manifestStore.ts delete mode 100644 applications/tari_walletd/web_ui/src/store/nftTransferStore.ts delete mode 100644 applications/tari_walletd/web_ui/src/store/themeStore.ts diff --git a/applications/tari_walletd/web_ui/src/api/helpers/types.ts b/applications/tari_walletd/web_ui/src/api/helpers/types.ts deleted file mode 100644 index ffd55f49ad..0000000000 --- a/applications/tari_walletd/web_ui/src/api/helpers/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -export interface ApiError { - message: string; - description: string; - statusCode: string | number; -} diff --git a/applications/tari_walletd/web_ui/src/api/hooks/useAccounts.ts b/applications/tari_walletd/web_ui/src/api/hooks/useAccounts.ts deleted file mode 100644 index 34526c6d46..0000000000 --- a/applications/tari_walletd/web_ui/src/api/hooks/useAccounts.ts +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import { useMutation, useQuery } from "@tanstack/react-query"; -import { - accountsClaimBurn, - accountsConfidentialTransfer, - accountsCreate, - accountsCreateFreeTestCoins, - accountsGet, - accountsGetBalances, - accountsGetDefault, - accountsList, - accountsStealthTransfer, - accountsTransfer, - mintFaucetNfts, - nftList, - validatorsGetFees, -} from "@utils/json_rpc"; -import { ApiError } from "@api/helpers/types"; -import queryClient from "@api/queryClient"; -import type { - AccountOrKeyIndex, - ClaimBurnProof, - ClaimBurnRequest, - ComponentAddress, - ComponentAddressOrName, - ConfidentialTransferInputSelection, - ResourceType, -} from "@tari-project/typescript-bindings"; - -const DEFAULT_MAX_FEE = 2000; - -// Fees are passed as strings because Amount is tagged -export const useAccountsClaimBurn = () => { - return useMutation({ - mutationFn: (params: ClaimBurnRequest) => accountsClaimBurn(params), - onError: (error: ApiError) => { - error; - }, - onSettled: () => { - queryClient.invalidateQueries({ queryKey: ["accounts"] }); - }, - }); -}; - -export type AccountsCreateMutate = { - accountName?: string; - isDefault?: boolean; - keyId?: number | null; -}; - -export const useAccountsCreate = () => { - return useMutation({ - mutationFn: async (req: AccountsCreateMutate) => { - return await accountsCreate({ - account_name: req.accountName || "", - is_default: req.isDefault || null, - key_id: req.keyId || null, - }); - }, - onError: (error: ApiError) => { - error; - }, - onSettled: () => { - queryClient.invalidateQueries({ queryKey: ["accounts"] }); - }, - }); -}; - -export interface TransferParams { - account: ComponentAddress; - amount: number; - resource_address: string; - destination_public_key: string; - max_fee: number | null; - resourceType: ResourceType; - output_to_revealed: boolean; - input_selection: ConfidentialTransferInputSelection; - badge: string | null; - dry_run: boolean; -} - -export const useAccountsTransfer = () => { - return useMutation({ - mutationFn: (params: TransferParams) => { - const account = { ComponentAddress: params.account }; - const max_fee = params.max_fee || DEFAULT_MAX_FEE; - if (params.resourceType === "Confidential") { - let transferRequest = { - account, - amount: params.amount, - resource_address: params.resource_address, - destination_public_key: params.destination_public_key, - max_fee, - proof_from_badge_resource: params.badge, - input_selection: params.input_selection, - output_to_revealed: params.output_to_revealed, - dry_run: params.dry_run, - }; - return accountsConfidentialTransfer(transferRequest); - } else if (params.resourceType === "Stealth") { - let transferRequest = { - owner_account: account, - input_selection: params.input_selection, - resource_address: params.resource_address, - destination_public_key: params.destination_public_key, - max_fee, - blinded_output_amount: params.output_to_revealed ? 0 : params.amount, - revealed_output_amount: params.output_to_revealed ? params.amount : 0, - dry_run: params.dry_run, - }; - return accountsStealthTransfer(transferRequest); - } else { - // Fungible and NFTs - let transferRequest = { - account, - amount: params.amount, - resource_address: params.resource_address, - destination_public_key: params.destination_public_key, - max_fee, - proof_from_badge_resource: params.badge, - input_selection: params.input_selection, - output_to_revealed: params.output_to_revealed, - dry_run: params.dry_run, - }; - return accountsTransfer(transferRequest); - } - }, - onError: (error: ApiError) => { - error; - }, - onSettled: () => { - queryClient.invalidateQueries({ queryKey: ["accounts"] }); - }, - }); -}; - -export const useAccountsCreateFreeTestCoins = () => { - const createFreeTestCoins = async ({ - account, - amount, - fee, - }: { - account: ComponentAddressOrName; - amount: number; - fee: number | null; - }) => - accountsCreateFreeTestCoins({ - account, - amount, - max_fee: fee, - }); - - return useMutation({ - mutationFn: createFreeTestCoins, - onError: (error: ApiError) => { - console.error(error); - }, - onSettled: () => { - queryClient.invalidateQueries({ queryKey: ["transactions"] }); - queryClient.invalidateQueries({ queryKey: ["accounts_balances"] }); - }, - }); -}; - -export const useMintTestnetFaucetNfts = () => { - const callApi = async ({ - account, - numberToMint, - mutableData, - maxFee, - }: { - account: ComponentAddressOrName; - numberToMint: number; - mutableData: object; - maxFee: number | null; - }) => - mintFaucetNfts({ - account, - mutable_data: mutableData, - number_to_mint: BigInt(numberToMint), - max_fee: maxFee, - }); - - return useMutation({ - mutationFn: callApi, - onError: (error: ApiError) => { - console.error(error); - }, - onSettled: () => { - queryClient.invalidateQueries({ queryKey: ["transactions"] }); - queryClient.invalidateQueries({ queryKey: ["accounts_balances"] }); - queryClient.invalidateQueries({ queryKey: ["nfts_list"] }); - }, - }); -}; - -export const useAccountsList = (offset: number, limit: number, enabled: boolean = true) => { - return useQuery({ - queryKey: ["accounts"], - queryFn: () => accountsList({ offset, limit }), - enabled, - }); -}; - -export const useAccountsGetBalances = (account: ComponentAddress, refresh: boolean = false) => { - return useQuery({ - queryKey: [`accounts_balances_${account}`], - queryFn: () => accountsGetBalances({ account: { ComponentAddress: account }, refresh }), - refetchInterval: 5000, - structuralSharing: (oldData, newData) => { - if (!oldData || !newData) return newData; - if (JSON.stringify(oldData) === JSON.stringify(newData)) { - return oldData; - } - return newData; - }, - }); -}; - -export const refreshAccountsBalances = (account: ComponentAddress) => { - return useMutation({ - mutationFn: () => accountsGetBalances({ account: { ComponentAddress: account }, refresh: true }), - onError: (error: ApiError) => { - error; - }, - onSettled: () => { - queryClient.invalidateQueries({ queryKey: ["accounts_balances_" + account] }); - }, - }); -}; - -export const useAccountsGetDefault = () => { - return useQuery({ - queryKey: ["accounts_get_default"], - queryFn: () => accountsGetDefault({}), - refetchInterval: false, - notifyOnChangeProps: ["data", "error"], - retryOnMount: false, - retry: false, - }); -}; -export const useAccountsGet = (account: ComponentAddress) => { - return useQuery({ - queryKey: ["accounts_get_" + account], - queryFn: () => accountsGet({ name_or_address: { ComponentAddress: account } }), - }); -}; - -export const useAccountNFTsList = (account: ComponentAddress, offset: number, limit: number) => { - return useQuery({ - queryKey: ["nfts_list", account, offset, limit], - queryFn: () => nftList({ account: { ComponentAddress: account }, offset, limit }), - }); -}; - -export const useValidatorFees = (accountOrKeyIndex: AccountOrKeyIndex, shardGroup = null) => { - return useQuery({ - queryKey: ["validator_fees"], - queryFn: () => validatorsGetFees({ account_or_key: accountOrKeyIndex, shard_group: shardGroup }), - }); -}; diff --git a/applications/tari_walletd/web_ui/src/api/hooks/useAuth.tsx b/applications/tari_walletd/web_ui/src/api/hooks/useAuth.tsx deleted file mode 100644 index eea4c22325..0000000000 --- a/applications/tari_walletd/web_ui/src/api/hooks/useAuth.tsx +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2025 The Tari Project -// SPDX-License-Identifier: BSD-3-Clause - -import { useQuery } from "@tanstack/react-query"; -import { authGetMethod } from "@utils/json_rpc"; - -export const useAuthMethod = () => { - return useQuery({ - queryKey: ["auth_method"], - queryFn: () => { - return authGetMethod(); - }, - }); -}; diff --git a/applications/tari_walletd/web_ui/src/api/hooks/useKeys.tsx b/applications/tari_walletd/web_ui/src/api/hooks/useKeys.tsx deleted file mode 100644 index 0791e373b5..0000000000 --- a/applications/tari_walletd/web_ui/src/api/hooks/useKeys.tsx +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import { useQuery, useMutation } from "@tanstack/react-query"; -import { ApiError } from "@api/helpers/types"; -import queryClient from "@api/queryClient"; -import { keysCreate, keysList, keysSetActive } from "@utils/json_rpc"; -import { KeyBranch } from "@tari-project/typescript-bindings"; - -export const useKeysList = (branch: KeyBranch) => { - return useQuery({ - queryKey: ["keys_list", branch], - queryFn: () => { - return keysList({ branch }); - }, - }); -}; - -export const useKeysCreate = (branch: KeyBranch) => { - return useMutation({ - mutationFn: () => keysCreate({ branch, specific_index: null }), - onError: (error: ApiError) => { - error; - }, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["keys_list"] }); - }, - }); -}; - -export const useKeysSetActive = () => { - const setActive = async (index: number) => { - const result = await keysSetActive({ index }); - return result; - }; - - return useMutation({ - mutationFn: setActive, - onError: (error: ApiError) => { - error; - }, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["keys_list"] }); - }, - }); -}; diff --git a/applications/tari_walletd/web_ui/src/api/hooks/useNfts.tsx b/applications/tari_walletd/web_ui/src/api/hooks/useNfts.tsx deleted file mode 100644 index 7e7d9f68a5..0000000000 --- a/applications/tari_walletd/web_ui/src/api/hooks/useNfts.tsx +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2025 The Tari Project -// SPDX-License-Identifier: BSD-3-Clause - -import { useMutation, useQuery } from "@tanstack/react-query"; -import { nftList, nftTransfer } from "@utils/json_rpc"; -import { ApiError } from "@api/helpers/types"; -import { TransferNftRequest } from "@tari-project/typescript-bindings"; -import queryClient from "@api/queryClient"; -import type { ComponentAddressOrName } from "@tari-project/typescript-bindings/dist"; - -export interface ListAccountNftsReq { - account: ComponentAddressOrName | null; - enabled?: boolean; -} - -export const useListNfts = (request: ListAccountNftsReq) => { - return useQuery({ - queryKey: ["list_nfts", request.account], - queryFn: async () => { - if (!request.account) { - return []; - } - const limit = 100; - let offset = 0; - let nfts = await nftList({ - account: request.account, - limit: limit, - offset: offset, - }); - let result = nfts.nfts; - while (nfts.nfts.length > 0) { - offset += limit; - nfts = await nftList({ - account: request.account, - limit: 1, - offset: offset, - }); - result = result.concat(nfts.nfts); - } - return result; - }, - enabled: request.enabled !== false && !!request.account, - retry: false, - }); -}; - -export const useNftsTransfer = (request: TransferNftRequest) => { - return useMutation({ - mutationFn: () => { - return nftTransfer(request); - }, - onError: (error: ApiError) => { - error; - }, - onSettled: () => { - // Invalidate all NFT-related queries - queryClient.invalidateQueries({ - predicate: (query) => { - const key = query.queryKey[0]; - return typeof key === "string" && ( - key === "nfts" || - key === "list_nfts" || - key.startsWith("nfts_list_") - ); - } - }); - }, - }); -}; diff --git a/applications/tari_walletd/web_ui/src/api/hooks/useTemplate.tsx b/applications/tari_walletd/web_ui/src/api/hooks/useTemplate.tsx deleted file mode 100644 index 7904de8bc5..0000000000 --- a/applications/tari_walletd/web_ui/src/api/hooks/useTemplate.tsx +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import { useQuery } from "@tanstack/react-query"; -import { templatesGet } from "@utils/json_rpc"; -import { TemplatesGetRequest } from "@tari-project/typescript-bindings"; - -export const useTemplateGet = (request: TemplatesGetRequest, options = {}) => { - return useQuery({ - queryKey: ["template_get", request], - queryFn: () => { - return templatesGet(request); - }, - refetchInterval: false, - notifyOnChangeProps: ["data", "error"], - retryOnMount: false, - retry: false, - ...options, - }); -}; diff --git a/applications/tari_walletd/web_ui/src/api/hooks/useTemplatesAuthored.tsx b/applications/tari_walletd/web_ui/src/api/hooks/useTemplatesAuthored.tsx deleted file mode 100644 index 59b2f968e5..0000000000 --- a/applications/tari_walletd/web_ui/src/api/hooks/useTemplatesAuthored.tsx +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2025 The Tari Project -// SPDX-License-Identifier: BSD-3-Clause - -import { useQuery } from "@tanstack/react-query"; -import { templatesListAuthored } from "@utils/json_rpc"; -import { TemplatesListAuthoredRequest } from "@tari-project/typescript-bindings"; - -export const useListTemplatesAuthored = (request: TemplatesListAuthoredRequest) => { - return useQuery({ - queryKey: ["templates_list_authored", request], - queryFn: () => { - return templatesListAuthored(request); - }, - refetchInterval: false, - notifyOnChangeProps: ["data", "error"], - retryOnMount: false, - retry: false, - }); -}; diff --git a/applications/tari_walletd/web_ui/src/api/hooks/useTokens.tsx b/applications/tari_walletd/web_ui/src/api/hooks/useTokens.tsx deleted file mode 100644 index 2e0077528f..0000000000 --- a/applications/tari_walletd/web_ui/src/api/hooks/useTokens.tsx +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import { useQuery, useMutation } from "@tanstack/react-query"; -import { ApiError } from "@api/helpers/types"; -import queryClient from "@api/queryClient"; -import { authGetAllJwt, authRevoke } from "@utils/json_rpc"; - -export const useGetAllTokens = () => { - return useQuery({ - queryKey: ["jwts_list"], - queryFn: () => { - return authGetAllJwt({}); - }, - }); -}; - -export const useAuthRevokeToken = () => { - const revokeToken = async (token: number) => { - const result = await authRevoke({ permission_token_id: token }); - return result; - }; - return useMutation({ - mutationFn: revokeToken, - onError: (error: ApiError) => { - error; - console.error(error); - }, - onSettled: () => { - queryClient.invalidateQueries({ queryKey: ["jwts_list"] }); - }, - }); -}; diff --git a/applications/tari_walletd/web_ui/src/api/hooks/useTransactions.tsx b/applications/tari_walletd/web_ui/src/api/hooks/useTransactions.tsx deleted file mode 100644 index 54728ec568..0000000000 --- a/applications/tari_walletd/web_ui/src/api/hooks/useTransactions.tsx +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import { useMutation, useQuery } from "@tanstack/react-query"; -import { - transactionsGet, - transactionsGetAll, - transactionsPublishTemplate, - transactionsSubmitManifest, - transactionsWaitResult, - validatorsGetFees, -} from "@utils/json_rpc"; -import { ApiError } from "@api/helpers/types"; -import queryClient from "@api/queryClient"; - -import type { AccountOrKeyIndex, TransactionGetAllRequest, TransactionStatus } from "@tari-project/typescript-bindings"; - -export const useTransactionDetails = (hash: string) => { - return useQuery({ - queryKey: ["transaction_details"], - queryFn: () => { - return transactionsGet({ transaction_id: hash }); - }, - }); -}; - -export const useGetAllTransactions = (req: TransactionGetAllRequest) => { - return useQuery({ - queryKey: ["transactions", req.status], - queryFn: () => transactionsGetAll(req), - refetchInterval: 5000, - placeholderData: (previousData) => previousData, - }); -}; - -export const usePublishTemplate = () => { - return useMutation({ - mutationFn: transactionsPublishTemplate, - onError: (error: ApiError) => { - console.error(error); - }, - onSettled: () => { - queryClient.invalidateQueries({ queryKey: ["transactions"] }); - queryClient.invalidateQueries({ queryKey: ["accounts_balances"] }); - }, - }); -}; -export const useSubmitManifest = () => { - return useMutation({ - mutationFn: transactionsSubmitManifest, - onError: (error: ApiError) => { - console.error(error); - }, - onSettled: () => { - queryClient.invalidateQueries({ queryKey: ["transactions"] }); - queryClient.invalidateQueries({ queryKey: ["accounts_balances"] }); - }, - }); -}; diff --git a/applications/tari_walletd/web_ui/src/api/hooks/useWebauthn.tsx b/applications/tari_walletd/web_ui/src/api/hooks/useWebauthn.tsx deleted file mode 100644 index bcb846fa2b..0000000000 --- a/applications/tari_walletd/web_ui/src/api/hooks/useWebauthn.tsx +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2025 The Tari Project -// SPDX-License-Identifier: BSD-3-Clause - -import { useQuery } from "@tanstack/react-query"; -import { webauthnAlreadyRegistered } from "@utils/json_rpc"; - -export const useWebauthnAlreadyRegistered = (username: string) => { - return useQuery({ - queryKey: ["webauthn_already_registered", username], - queryFn: () => { - return webauthnAlreadyRegistered(username); - }, - refetchInterval: false, - notifyOnChangeProps: ["data", "error"], - retryOnMount: false, - retry: false, - }); -}; diff --git a/applications/tari_walletd/web_ui/src/api/queryClient.ts b/applications/tari_walletd/web_ui/src/api/queryClient.ts deleted file mode 100644 index 8ce308c767..0000000000 --- a/applications/tari_walletd/web_ui/src/api/queryClient.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import { QueryClient } from "@tanstack/react-query"; - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - retry: 3, - structuralSharing: true, - }, - }, -}); - -export default queryClient; diff --git a/applications/tari_walletd/web_ui/src/store/accountStore.ts b/applications/tari_walletd/web_ui/src/store/accountStore.ts deleted file mode 100644 index 3b4f34534b..0000000000 --- a/applications/tari_walletd/web_ui/src/store/accountStore.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import { create } from "zustand"; -import { Account } from "@tari-project/typescript-bindings"; - -interface Store { - showBalance: boolean; - setShowBalance: (show: boolean) => void; - account: Account | null; - setAccount: (account: Account) => void; - publicKey: string; - setPublicKey: (publicKey: string) => void; - indexer: string; - setIndexer: (indexer: string) => void; - popup: any; - setPopup: (popup: any) => void; -} - -const useAccountStore = create()((set) => ({ - showBalance: true, - setShowBalance: (show) => set({ showBalance: show }), - account: null, - setAccount: (account) => set({ account: account }), - publicKey: "", - setPublicKey: (publicKey) => set({ publicKey: publicKey }), - indexer: "", - setIndexer: (indexer) => set({ indexer: indexer }), - popup: { visible: false }, - setPopup: (popup) => set({ popup: { visible: true, ...popup } }), -})); - -export default useAccountStore; diff --git a/applications/tari_walletd/web_ui/src/store/authStore.ts b/applications/tari_walletd/web_ui/src/store/authStore.ts deleted file mode 100644 index b25544deb5..0000000000 --- a/applications/tari_walletd/web_ui/src/store/authStore.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2025 The Tari Project -// SPDX-License-Identifier: BSD-3-Clause - -import { create } from "zustand"; -import { persist } from "zustand/middleware"; -import { AUTH_TOKEN_FOR_NONE_AUTH } from "@routes/Auth/Auth"; - -interface Store { - username: string; - authToken: string; - setAuthToken: (token: string) => void; - clearToken: () => void; -} - -const useAuthStore = create()( - persist( - (set) => ({ - username: "tari-wallet-webui", - authToken: "", - setAuthToken: (token) => set({ authToken: token }), - clearToken: () => - set((s) => { - if (s.authToken === AUTH_TOKEN_FOR_NONE_AUTH) { - return {}; - } - return { authToken: "" }; - }), - }), - { name: "tari-auth" }, - ), -); - -export default useAuthStore; diff --git a/applications/tari_walletd/web_ui/src/store/flowEditorStore.ts b/applications/tari_walletd/web_ui/src/store/flowEditorStore.ts deleted file mode 100644 index b64965ed17..0000000000 --- a/applications/tari_walletd/web_ui/src/store/flowEditorStore.ts +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2025. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import { create } from "zustand"; -import { GeneratedCodeType } from "@tari-project/tari-extension-common"; -import { AccountInfo } from "@tari-project/typescript-bindings"; -import { persist } from "zustand/middleware"; - -interface FlowEditorStore { - panelOpen: boolean; - setPanelOpen: (open: boolean) => void; - templateId: string; - setTemplateId: (id: string) => void; - codeDialogOpen: boolean; - setCodeDialogOpen: (open: boolean) => void; - generatedCode: string; - setGeneratedCode: (code: string) => void; - generatedCodeType: GeneratedCodeType | null; - setGeneratedCodeType: (type: GeneratedCodeType | null) => void; - account: AccountInfo | undefined; - setAccount: (account: AccountInfo | undefined) => void; - setCurrentState: (state: any) => void; - currentState: any; - fee: number; - setFee: (fee: number) => void; -} - -export const INITIAL_FLOW_STATE = { - $schema: "https://tari-project.github.io/tari-vscode-nocode-extension/schemas/tari-schema-v1.0.json", - version: "1.0", - nodes: [], - edges: [], -}; - -const useFlowEditorStore = create()( - persist( - (set) => ({ - panelOpen: true, - setPanelOpen: (open) => set({ panelOpen: open }), - templateId: "", - setTemplateId: (id) => set({ templateId: id }), - codeDialogOpen: false, - setCodeDialogOpen: (open) => set({ codeDialogOpen: open }), - generatedCode: "", - setGeneratedCode: (code) => set({ generatedCode: code }), - generatedCodeType: null, - setGeneratedCodeType: (type) => set({ generatedCodeType: type }), - account: undefined, - setAccount: (account) => set({ account }), - currentState: INITIAL_FLOW_STATE, - setCurrentState: (json) => set({ currentState: json }), - fee: 3000, - setFee: (fee) => set({ fee }), - }), - { name: "flowEditor" }, - ), -); - -export default useFlowEditorStore; diff --git a/applications/tari_walletd/web_ui/src/store/manifestStore.ts b/applications/tari_walletd/web_ui/src/store/manifestStore.ts deleted file mode 100644 index efa7e103f0..0000000000 --- a/applications/tari_walletd/web_ui/src/store/manifestStore.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2025. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import { create } from "zustand"; -import { persist } from "zustand/middleware"; - -const DEFAULT_CODE = ` -// use template_xxx as TemplateName; - -fn main() { - // TemplateName::call_something(); - // let account = var!["account"]; - // let bucket = account.withdraw(1000); -}`; - -interface Store { - code: string; - setCode: (code: string) => void; - variables: Record; - addVariable: (key: string, value: string) => void; - removeVariable: (key: string) => void; -} - -const useManifestCodeStore = create()( - persist( - (set) => ({ - code: DEFAULT_CODE, - setCode: (code: string) => set(() => ({ code })), - variables: {}, - addVariable: (key: string, value: string) => - set((state) => ({ - variables: { - ...state.variables, - [key]: value, - }, - })), - removeVariable: (key: string) => - set((state) => { - const { [key]: _, ...rest } = state.variables; - return { variables: rest }; - }), - }), - { name: "manifest-code" }, - ), -); - -export default useManifestCodeStore; diff --git a/applications/tari_walletd/web_ui/src/store/nftTransferStore.ts b/applications/tari_walletd/web_ui/src/store/nftTransferStore.ts deleted file mode 100644 index de30837760..0000000000 --- a/applications/tari_walletd/web_ui/src/store/nftTransferStore.ts +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2025. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import { create } from 'zustand'; -import type { NonFungibleId, ResourceAddress } from '@tari-project/typescript-bindings'; - -export type DialogStep = 'form' | 'confirmation' | 'result'; - -interface TransferFormState { - payerAccount: string; - nfts: NonFungibleId[]; - targetAccountPublicKey: string; - maxFee: string; - resourceAddress: ResourceAddress; -} - -interface TransferResult { - success: boolean; - message: string; -} - -interface Validity { - payerAccount: boolean; - nfts: boolean; - targetAccountPublicKey: boolean; -} - -interface NftTransferState { - // Dialog state - currentStep: DialogStep; - disabled: boolean; - - // Form state - transferFormState: TransferFormState; - validity: Validity; - - // Fee estimation state - estimatedFee: number | null; - isEstimatingFee: boolean; - - // Result state - transferResult: TransferResult | null; - - // Auto-close state - autoCloseTimeoutId: NodeJS.Timeout | null; - - // Actions - setCurrentStep: (step: DialogStep) => void; - setDisabled: (disabled: boolean) => void; - setTransferFormState: (state: Partial) => void; - setValidity: (validity: Partial) => void; - setEstimatedFee: (fee: number | null) => void; - setIsEstimatingFee: (estimating: boolean) => void; - setTransferResult: (result: TransferResult | null) => void; - setAutoCloseTimeoutId: (timeoutId: NodeJS.Timeout | null) => void; - - // Complex actions - updateFormValue: (name: string, value: string, isValid?: boolean) => void; - initializeFormState: (preSelectedNftId?: NonFungibleId, preSelectedResourceAddress?: ResourceAddress, accountAddress?: string) => void; - resetState: (preSelectedNftId?: NonFungibleId, preSelectedResourceAddress?: ResourceAddress) => void; - isFormValid: () => boolean; -} - -const createInitialFormState = ( - preSelectedNftId?: NonFungibleId, - preSelectedResourceAddress?: ResourceAddress, - accountAddress?: string -): TransferFormState => ({ - payerAccount: accountAddress || "", - nfts: preSelectedNftId ? [preSelectedNftId] : [], - targetAccountPublicKey: "", - maxFee: "", - resourceAddress: (preSelectedResourceAddress || "") as ResourceAddress, -}); - -const createInitialValidity = (preSelectedNftId?: NonFungibleId): Validity => ({ - payerAccount: true, - nfts: preSelectedNftId ? true : false, - targetAccountPublicKey: false, -}); - -export const useNftTransferStore = create((set, get) => ({ - // Initial state - currentStep: 'form', - disabled: false, - transferFormState: createInitialFormState(), - validity: createInitialValidity(), - estimatedFee: null, - isEstimatingFee: false, - transferResult: null, - autoCloseTimeoutId: null, - - // Simple setters - setCurrentStep: (step) => set({ currentStep: step }), - setDisabled: (disabled) => set({ disabled }), - setTransferFormState: (state) => set((prev) => ({ - transferFormState: { ...prev.transferFormState, ...state } - })), - setValidity: (validity) => set((prev) => ({ - validity: { ...prev.validity, ...validity } - })), - setEstimatedFee: (fee) => set({ estimatedFee: fee }), - setIsEstimatingFee: (estimating) => set({ isEstimatingFee: estimating }), - setTransferResult: (result) => set({ transferResult: result }), - setAutoCloseTimeoutId: (timeoutId) => set({ autoCloseTimeoutId: timeoutId }), - - // Complex actions - updateFormValue: (name, value, isValid) => { - const { transferFormState, validity } = get(); - - set({ - transferFormState: { ...transferFormState, [name]: value }, - validity: isValid !== undefined ? { ...validity, [name]: isValid } : validity - }); - }, - - initializeFormState: (preSelectedNftId, preSelectedResourceAddress, accountAddress) => { - set({ - transferFormState: createInitialFormState(preSelectedNftId, preSelectedResourceAddress, accountAddress), - validity: createInitialValidity(preSelectedNftId), - }); - }, - - resetState: (preSelectedNftId, preSelectedResourceAddress) => { - const { autoCloseTimeoutId } = get(); - - // Clear any active timeout - if (autoCloseTimeoutId) { - clearTimeout(autoCloseTimeoutId); - } - - set({ - currentStep: 'form', - disabled: false, - transferFormState: createInitialFormState(preSelectedNftId, preSelectedResourceAddress), - validity: createInitialValidity(preSelectedNftId), - estimatedFee: null, - isEstimatingFee: false, - transferResult: null, - autoCloseTimeoutId: null, - }); - }, - - isFormValid: () => { - const { validity } = get(); - return Object.values(validity).every((v) => v); - }, -})); \ No newline at end of file diff --git a/applications/tari_walletd/web_ui/src/store/themeStore.ts b/applications/tari_walletd/web_ui/src/store/themeStore.ts deleted file mode 100644 index ef780fcbdd..0000000000 --- a/applications/tari_walletd/web_ui/src/store/themeStore.ts +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import { create } from "zustand"; -import { persist } from "zustand/middleware"; - -interface Store { - themeMode: "light" | "dark"; - setThemeMode: (mode: "light" | "dark") => void; -} - -const useThemeStore = create()( - persist( - (set) => ({ - themeMode: "light", - setThemeMode: (mode) => set({ themeMode: mode }), - }), - { - name: "tari-theme", - }, - ), -); - -export default useThemeStore; diff --git a/applications/tari_walletd/web_ui/tsconfig.json b/applications/tari_walletd/web_ui/tsconfig.json index 710c4257bf..196e012ae4 100644 --- a/applications/tari_walletd/web_ui/tsconfig.json +++ b/applications/tari_walletd/web_ui/tsconfig.json @@ -30,8 +30,8 @@ "@utils/*": ["./src/utils/*"], "@assets/*": ["./src/assets/*"], "@hooks/*": ["./src/hooks/*"], - "@api/*": ["./src/api/*"], - "@store/*": ["./src/store/*"], + "@api/*": ["./src/services/api/*"], + "@store/*": ["./src/services/store/*"], "@theme/*": ["./src/theme/*"] } }, diff --git a/applications/tari_walletd/web_ui/vite.config.ts b/applications/tari_walletd/web_ui/vite.config.ts index 0947ee6a5d..8307d88729 100644 --- a/applications/tari_walletd/web_ui/vite.config.ts +++ b/applications/tari_walletd/web_ui/vite.config.ts @@ -35,8 +35,8 @@ export default defineConfig({ "@utils": path.resolve(__dirname, "./src/utils"), "@assets": path.resolve(__dirname, "./src/assets"), "@hooks": path.resolve(__dirname, "./src/hooks"), - "@api": path.resolve(__dirname, "./src/api"), - "@store": path.resolve(__dirname, "./src/store"), + "@api": path.resolve(__dirname, "./src/services/api"), + "@store": path.resolve(__dirname, "./src/services/store"), "@theme": path.resolve(__dirname, "./src/theme"), }, }, From 5416650da068b399cfa1068a12e4462aa87c4269 Mon Sep 17 00:00:00 2001 From: Erika Date: Thu, 11 Sep 2025 14:47:04 +0200 Subject: [PATCH 03/10] feat: improve fee display --- .../src/routes/Transactions/FeeReceipt.tsx | 5 ++-- .../Transactions/TransactionDetails.tsx | 25 +++++++++++++------ .../src/routes/Transactions/Transactions.tsx | 6 +++-- .../tari_walletd/web_ui/src/utils/helpers.tsx | 4 --- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/applications/tari_walletd/web_ui/src/routes/Transactions/FeeReceipt.tsx b/applications/tari_walletd/web_ui/src/routes/Transactions/FeeReceipt.tsx index 1e68a5ca3c..99aa18b803 100644 --- a/applications/tari_walletd/web_ui/src/routes/Transactions/FeeReceipt.tsx +++ b/applications/tari_walletd/web_ui/src/routes/Transactions/FeeReceipt.tsx @@ -22,6 +22,7 @@ import { TableContainer, Table, TableHead, TableRow, TableCell, TableBody, Box, Typography, Chip } from "@mui/material"; import { DataTableCell } from "@components/StyledComponents"; +import { formatCurrency } from "@/utils/helpers"; export default function FeeReceipt({ data }: { data: any }) { if (!data) { @@ -35,8 +36,8 @@ export default function FeeReceipt({ data }: { data: any }) { } const feeItems = [ - { label: "Total Fee Payment", value: data.total_fee_payment, color: "primary" as const }, - { label: "Total Fees Paid", value: data.total_fees_paid, color: "success" as const }, + { label: "Total Fee Payment", value: formatCurrency(data.total_fee_payment), color: "primary" as const }, + { label: "Total Fees Paid", value: formatCurrency(data.total_fees_paid), color: "success" as const }, ]; const costBreakdownItems = data.cost_breakdown?.breakdown diff --git a/applications/tari_walletd/web_ui/src/routes/Transactions/TransactionDetails.tsx b/applications/tari_walletd/web_ui/src/routes/Transactions/TransactionDetails.tsx index 35c69af592..7711d77185 100644 --- a/applications/tari_walletd/web_ui/src/routes/Transactions/TransactionDetails.tsx +++ b/applications/tari_walletd/web_ui/src/routes/Transactions/TransactionDetails.tsx @@ -24,7 +24,18 @@ import { useState } from "react"; import { useParams } from "react-router-dom"; import { useTransactionDetails } from "@api/hooks/useTransactions"; import { Accordion, AccordionDetails, AccordionSummary } from "@components/Accordion"; -import { Grid, Table, TableContainer, TableBody, TableRow, TableCell, Button, Fade, Stack } from "@mui/material"; +import { + Grid, + Table, + TableContainer, + TableBody, + TableRow, + TableCell, + Button, + Fade, + Stack, + Tooltip, +} from "@mui/material"; import Typography from "@mui/material/Typography"; import { saveAs } from "file-saver"; import { DataTableCell, StyledPaper } from "@components/StyledComponents"; @@ -45,6 +56,7 @@ import Error from "@components/Error"; import { FinalizeResult, TransactionResult, TransactionSignature } from "@tari-project/typescript-bindings"; import { getRejectReasonFromTransactionResult, rejectReasonToString } from "@tari-project/typescript-bindings"; import { BsQuestionCircle } from "react-icons/bs"; +import { formatCurrency } from "@/utils/helpers"; export default function TransactionDetails() { const [expandedPanels, setExpandedPanels] = useState([]); @@ -150,15 +162,14 @@ export default function TransactionDetails() { Total Fees - {feeReceipt?.total_fees_paid.toString() || 0} + {feeReceipt ? formatCurrency(feeReceipt.total_fees_paid) : "0"} {feeReceipt?.total_fee_overcharge ? ( <> {" "} - ({feeReceipt.total_fee_overcharge} overcharge{" "} - + ({formatCurrency(feeReceipt.total_fee_overcharge)} overcharge{" "} + + + ) ) : ( diff --git a/applications/tari_walletd/web_ui/src/routes/Transactions/Transactions.tsx b/applications/tari_walletd/web_ui/src/routes/Transactions/Transactions.tsx index 58393a874a..bee340de55 100644 --- a/applications/tari_walletd/web_ui/src/routes/Transactions/Transactions.tsx +++ b/applications/tari_walletd/web_ui/src/routes/Transactions/Transactions.tsx @@ -40,7 +40,7 @@ import FetchStatusCheck from "@components/FetchStatusCheck"; import StatusChip from "@components/StatusChip"; import { DataTableCell } from "@components/StyledComponents"; import { useGetAllTransactions } from "@api/hooks/useTransactions"; -import { emptyRows, handleChangePage, handleChangeRowsPerPage } from "@utils/helpers"; +import { emptyRows, handleChangePage, handleChangeRowsPerPage, formatCurrency } from "@utils/helpers"; import { Account, WalletTransaction } from "@tari-project/typescript-bindings"; import TimeChip from "./TimeChip"; @@ -101,7 +101,9 @@ export default function Transactions({ account }: { account: Account; ownerPubli - {fee_receipt?.total_fees_paid.toString() || "--"} + + {fee_receipt?.total_fees_paid ? formatCurrency(fee_receipt.total_fees_paid) : "--"} + { if (typeof amount === "bigint") { - // Handle bigint: divide by divisor to get integer and remainder for fractional part const divisor = BigInt(CURRENCY.DIVISOR); const integerPart = amount / divisor; const remainder = amount % divisor; - // Convert remainder to fractional string padded to CURRENCY.DECIMALS const fractionalPart = remainder.toString().padStart(CURRENCY.DECIMALS, "0"); return `${Number(integerPart).toLocaleString("en-US")}.${fractionalPart} ${CURRENCY.SYMBOL}`; } else if (typeof amount === "number") { - // Handle number: guard against NaN and use existing toFixed logic if (isNaN(amount)) { return `0 ${CURRENCY.SYMBOL}`; } const convertedAmount = amount / CURRENCY.DIVISOR; return `${convertedAmount.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: CURRENCY.DECIMALS })} ${CURRENCY.SYMBOL}`; } else { - // Handle invalid types return `0 ${CURRENCY.SYMBOL}`; } }; From c5ddd5079e47b6aadb2e53758ce1b2ca0693c9c7 Mon Sep 17 00:00:00 2001 From: Erika Date: Fri, 12 Sep 2025 13:06:27 +0200 Subject: [PATCH 04/10] feat: set default currency --- applications/tari_walletd/web_ui/src/App.tsx | 3 ++ .../src/routes/AssetVault/Tokens/Tokens.tsx | 16 ++++--- .../src/routes/FlowEditor/FlowEditor.tsx | 8 ++-- .../Settings/Components/GeneralSettings.tsx | 2 +- .../src/services/store/currencyStore.ts | 35 +++++++++++++++ .../services/store/hooks/useCurrencySync.ts | 43 +++++++++++++++++++ .../tari_walletd/web_ui/src/utils/helpers.tsx | 17 +++++--- 7 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 applications/tari_walletd/web_ui/src/services/store/currencyStore.ts create mode 100644 applications/tari_walletd/web_ui/src/services/store/hooks/useCurrencySync.ts diff --git a/applications/tari_walletd/web_ui/src/App.tsx b/applications/tari_walletd/web_ui/src/App.tsx index b3074da0cc..c3c6d2701c 100644 --- a/applications/tari_walletd/web_ui/src/App.tsx +++ b/applications/tari_walletd/web_ui/src/App.tsx @@ -42,6 +42,7 @@ import { jwtDecode } from "jwt-decode"; import Templates from "@routes/Templates/Templates"; import Manifest from "@routes/Manifest/Manifest"; import FlowEditor from "@routes/FlowEditor/FlowEditor"; +import { useCurrencySync } from "@store/hooks/useCurrencySync"; export const breadcrumbRoutes = [ { @@ -160,6 +161,8 @@ function App() { const { authToken } = authStore; let isAuthenticated = !!authToken; + useCurrencySync(); + useEffect(() => { if (isTokenExpired(authToken) && authToken !== AUTH_TOKEN_FOR_NONE_AUTH) { authStore.clearToken(); diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx index d628fb2a94..f95997ddaf 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx @@ -139,13 +139,14 @@ function Tokens({ account }: { account: Account }) { resource_address={resourceToSend?.address} resource_type={resourceToSend?.resource_type!} token_symbol={ - balancesData?.balances.find((b) => b.resource_address === resourceToSend?.address)?.token_symbol || "" + balancesData?.balances.find((b: BalanceEntry) => b.resource_address === resourceToSend?.address) + ?.token_symbol || "" } /> @@ -181,7 +182,12 @@ function Tokens({ account }: { account: Account }) { confidential_balance={confidential_balance} vault_address={vault_address ?? undefined} // convert null to undefined divisibility={divisibility} - onSendClicked={handleSendResourceClicked} + onSendClicked={ + handleSendResourceClicked as ( + resource_address: ResourceAddress, + resource_type: ResourceType, + ) => void + } /> ), )} diff --git a/applications/tari_walletd/web_ui/src/routes/FlowEditor/FlowEditor.tsx b/applications/tari_walletd/web_ui/src/routes/FlowEditor/FlowEditor.tsx index a9bcc1058d..28fd75a58f 100644 --- a/applications/tari_walletd/web_ui/src/routes/FlowEditor/FlowEditor.tsx +++ b/applications/tari_walletd/web_ui/src/routes/FlowEditor/FlowEditor.tsx @@ -161,7 +161,7 @@ function FlowEditor() { useEffect(() => { if (dataAccountsList?.accounts && dataAccountsList.accounts.length > 0) { - const defaultAcc = dataAccountsList.accounts.find((acc) => acc.account.is_default); + const defaultAcc = dataAccountsList.accounts.find((acc: any) => acc.account.is_default); setAccount(defaultAcc || dataAccountsList.accounts[0]); } }, [dataAccountsList]); @@ -178,7 +178,7 @@ function FlowEditor() { const onAccountChange = (e: SelectChangeEvent) => { const selected = dataAccountsList?.accounts.find( - (acc) => substateIdToString(acc.account.address) === e.target.value, + (acc: any) => substateIdToString(acc.account.address) === e.target.value, ); setAccount(selected); }; @@ -322,7 +322,7 @@ function FlowEditor() { value={account ? substateIdToString(account.account.address) : ""} onChange={onAccountChange} > - {dataAccountsList?.accounts?.map((acc) => ( + {dataAccountsList?.accounts?.map((acc: any) => ( {acc.account.name || substateIdToString(acc.account.address)} @@ -415,7 +415,7 @@ function FlowEditor() { )} - {methods.map((m, i) => ( + {methods.map((m: any, i: number) => ( void; +} + +const useCurrencyStore = create((set) => ({ + currencySymbol: "XTR", + setCurrencySymbol: (symbol) => set({ currencySymbol: symbol }), +})); + +export default useCurrencyStore; diff --git a/applications/tari_walletd/web_ui/src/services/store/hooks/useCurrencySync.ts b/applications/tari_walletd/web_ui/src/services/store/hooks/useCurrencySync.ts new file mode 100644 index 0000000000..bdbbe5fb75 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/services/store/hooks/useCurrencySync.ts @@ -0,0 +1,43 @@ +// Copyright 2025. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { useEffect } from "react"; +import { useAccountsGetBalances, useAccountsGetDefault } from "@api/hooks/useAccounts"; +import { substateIdToString } from "@utils/helpers"; +import useCurrencyStore from "@store/currencyStore"; + +export const useCurrencySync = () => { + const { data: defaultAccount } = useAccountsGetDefault(); + const { data: balancesData, isLoading } = useAccountsGetBalances( + defaultAccount ? substateIdToString(defaultAccount.account.address) : "" + ); + const { setCurrencySymbol } = useCurrencyStore(); + + useEffect(() => { + if (defaultAccount && balancesData?.balances && balancesData.balances.length > 0) { + const mainBalance = balancesData.balances[0]; + if (mainBalance.token_symbol) { + setCurrencySymbol(mainBalance.token_symbol); + } + } + }, [defaultAccount, balancesData?.balances, isLoading, setCurrencySymbol]); +}; diff --git a/applications/tari_walletd/web_ui/src/utils/helpers.tsx b/applications/tari_walletd/web_ui/src/utils/helpers.tsx index f6e5af1e85..fab6305ba0 100644 --- a/applications/tari_walletd/web_ui/src/utils/helpers.tsx +++ b/applications/tari_walletd/web_ui/src/utils/helpers.tsx @@ -23,6 +23,7 @@ import { ChangeEvent } from "react"; import type { Amount, SubstateId, NonFungibleId } from "@tari-project/typescript-bindings"; import { CURRENCY } from "@utils/constants"; +import useCurrencyStore from "@store/currencyStore"; export const renderJson = (json: any) => { if (Array.isArray(json)) { @@ -251,6 +252,8 @@ export function bigintToDecimalString(int: bigint | Amount, decimalPlaces: numbe } export const formatCurrency = (amount: number | bigint): string => { + const currencySymbol = useCurrencyStore.getState().currencySymbol; + if (typeof amount === "bigint") { const divisor = BigInt(CURRENCY.DIVISOR); const integerPart = amount / divisor; @@ -258,24 +261,26 @@ export const formatCurrency = (amount: number | bigint): string => { const fractionalPart = remainder.toString().padStart(CURRENCY.DECIMALS, "0"); - return `${Number(integerPart).toLocaleString("en-US")}.${fractionalPart} ${CURRENCY.SYMBOL}`; + return `${Number(integerPart).toLocaleString("en-US")}.${fractionalPart} ${currencySymbol}`; } else if (typeof amount === "number") { if (isNaN(amount)) { - return `0 ${CURRENCY.SYMBOL}`; + return `0 ${currencySymbol}`; } const convertedAmount = amount / CURRENCY.DIVISOR; - return `${convertedAmount.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: CURRENCY.DECIMALS })} ${CURRENCY.SYMBOL}`; + return `${convertedAmount.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: CURRENCY.DECIMALS })} ${currencySymbol}`; } else { - return `0 ${CURRENCY.SYMBOL}`; + return `0 ${currencySymbol}`; } }; // Helper function for formatting amounts that are already in display units (XTR) export const formatDisplayCurrency = (amount: number): string => { + const currencySymbol = useCurrencyStore.getState().currencySymbol; + if (isNaN(amount)) { - return `0 ${CURRENCY.SYMBOL}`; + return `0 ${currencySymbol}`; } - return `${amount.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: CURRENCY.DECIMALS })} ${CURRENCY.SYMBOL}`; + return `${amount.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: CURRENCY.DECIMALS })} ${currencySymbol}`; }; export function validateHash(hash: string): boolean { From 4d5d128906e75ea2e204823f86b12cd49496a63f Mon Sep 17 00:00:00 2001 From: Erika Date: Fri, 12 Sep 2025 13:35:05 +0200 Subject: [PATCH 05/10] feat: add token and nft placeholders and fix nft invalidation --- .../src/routes/AssetVault/NFTs/NFTList.tsx | 22 +++- .../NFTs/components/ClaimNftsButton.tsx | 12 +- .../src/routes/AssetVault/Tokens/Tokens.tsx | 116 +++++++++++------- .../web_ui/src/services/api/hooks/useNfts.tsx | 2 +- 4 files changed, 96 insertions(+), 56 deletions(-) diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/NFTList.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/NFTList.tsx index 4286bf1738..f3ca8fc926 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/NFTList.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/NFTList.tsx @@ -31,6 +31,8 @@ import { TableHead, TablePagination, TableRow, + Typography, + Box, } from "@mui/material"; import type { ListNftsResponse, NonFungibleToken } from "@tari-project/typescript-bindings"; import React, { useState } from "react"; @@ -105,7 +107,25 @@ export default function NFTList(props: NftListProps) { - {viewMode === "grid" ? ( + {displayedNfts.length === 0 && !nftsListIsFetching ? ( + + + No NFTs found + + + You don't have any NFTs in this account yet. Try claiming some testnet NFTs to get started. + + + ) : viewMode === "grid" ? ( {displayedNfts.map((nft: NonFungibleToken, index: number) => ( diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/ClaimNftsButton.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/ClaimNftsButton.tsx index 600203b4a4..54bc89b84a 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/ClaimNftsButton.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/ClaimNftsButton.tsx @@ -45,18 +45,14 @@ function ClaimNftsButton() { maxFee: 2000, }, { - onSuccess: (resp) => { + onSuccess: (resp: any) => { console.log(resp); // Invalidate NFT queries to refresh the list - queryClient.invalidateQueries({ + queryClient.invalidateQueries({ predicate: (query) => { const key = query.queryKey[0]; - return typeof key === "string" && ( - key === "nfts" || - key === "list_nfts" || - key.startsWith("nfts_list_") - ); - } + return typeof key === "string" && (key === "nfts" || key === "list_nfts" || key === "nfts_list"); + }, }); }, }, diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx index f95997ddaf..7ce8001a66 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx @@ -26,6 +26,7 @@ import TableCell from "@mui/material/TableCell"; import TableContainer from "@mui/material/TableContainer"; import TableHead from "@mui/material/TableHead"; import TableRow from "@mui/material/TableRow"; +import { Typography, Box } from "@mui/material"; import { useState } from "react"; import FetchStatusCheck from "@components/FetchStatusCheck"; import { DataTableCell } from "@components/StyledComponents"; @@ -130,6 +131,9 @@ function Tokens({ account }: { account: Account }) { const handleSendResourceClicked = (address: ResourceAddress, resource_type: ResourceType) => { setResourceToSend({ address, resource_type }); }; + + const hasBalances = balancesData?.balances && balancesData.balances.length > 0; + return ( <> - -
- - - Vault - Resource - Revealed Balance - Confidential Balance - - - - - {balancesData?.balances.map( - ( - { - resource_address, - balance, - resource_type, - confidential_balance, - token_symbol, - vault_address, - divisibility, - }: BalanceEntry, - i: number, - ) => ( - void - } - /> - ), - )} - -
-
+ {!hasBalances && !balancesIsFetching ? ( + + + No tokens found + + + This account doesn't have any tokens yet. You can receive tokens by sharing your account address or get testnet tokens from a faucet. + + + ) : ( + + + + + Vault + Resource + Revealed Balance + Confidential Balance + + + + + {balancesData?.balances.map( + ( + { + resource_address, + balance, + resource_type, + confidential_balance, + token_symbol, + vault_address, + divisibility, + }: BalanceEntry, + i: number, + ) => ( + void + } + /> + ), + )} + +
+
+ )}
); diff --git a/applications/tari_walletd/web_ui/src/services/api/hooks/useNfts.tsx b/applications/tari_walletd/web_ui/src/services/api/hooks/useNfts.tsx index 7e7d9f68a5..aee22c17fd 100644 --- a/applications/tari_walletd/web_ui/src/services/api/hooks/useNfts.tsx +++ b/applications/tari_walletd/web_ui/src/services/api/hooks/useNfts.tsx @@ -60,7 +60,7 @@ export const useNftsTransfer = (request: TransferNftRequest) => { return typeof key === "string" && ( key === "nfts" || key === "list_nfts" || - key.startsWith("nfts_list_") + key === "nfts_list" ); } }); From a4a360d19cd33396bf9000a6b40e1cea32fad54d Mon Sep 17 00:00:00 2001 From: Erika Date: Fri, 12 Sep 2025 14:05:24 +0200 Subject: [PATCH 06/10] feat: add error notifications --- applications/tari_walletd/web_ui/src/App.tsx | 11 +- .../src/contexts/ErrorNotificationContext.tsx | 103 ++++++++++++++++++ .../NFTs/components/ClaimNftsButton.tsx | 19 +++- 3 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 applications/tari_walletd/web_ui/src/contexts/ErrorNotificationContext.tsx diff --git a/applications/tari_walletd/web_ui/src/App.tsx b/applications/tari_walletd/web_ui/src/App.tsx index c3c6d2701c..0c2384ff5e 100644 --- a/applications/tari_walletd/web_ui/src/App.tsx +++ b/applications/tari_walletd/web_ui/src/App.tsx @@ -43,6 +43,7 @@ import Templates from "@routes/Templates/Templates"; import Manifest from "@routes/Manifest/Manifest"; import FlowEditor from "@routes/FlowEditor/FlowEditor"; import { useCurrencySync } from "@store/hooks/useCurrencySync"; +import { ErrorNotificationProvider } from "./contexts/ErrorNotificationContext"; export const breadcrumbRoutes = [ { @@ -196,10 +197,11 @@ function App() { }, [authMethod, authMethodsIsError]); return ( -
- - }> - } /> + +
+ + }> + } /> } /> } />
+
); } diff --git a/applications/tari_walletd/web_ui/src/contexts/ErrorNotificationContext.tsx b/applications/tari_walletd/web_ui/src/contexts/ErrorNotificationContext.tsx new file mode 100644 index 0000000000..ac16b02e17 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/contexts/ErrorNotificationContext.tsx @@ -0,0 +1,103 @@ +// Copyright 2025. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import React, { createContext, useContext, useState, useRef } from "react"; +import { Snackbar, Alert, AlertColor } from "@mui/material"; + +interface ErrorNotification { + message: string; + severity?: AlertColor; + duration?: number; +} + +interface ErrorNotificationContextType { + showError: (message: string, severity?: AlertColor, duration?: number) => void; + showSuccess: (message: string, duration?: number) => void; + showWarning: (message: string, duration?: number) => void; + showInfo: (message: string, duration?: number) => void; + clearNotification: () => void; +} + +const ErrorNotificationContext = createContext(undefined); + +export const ErrorNotificationProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [notification, setNotification] = useState(null); + const timeoutRef = useRef(null); + + const showNotification = (message: string, severity: AlertColor = "error", duration: number = 8000) => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + setNotification({ message, severity, duration }); + + timeoutRef.current = setTimeout(() => { + setNotification(null); + }, duration); + }; + + const clearNotification = () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + setNotification(null); + }; + + const contextValue: ErrorNotificationContextType = { + showError: (message, severity = "error", duration = 8000) => showNotification(message, severity, duration), + showSuccess: (message, duration = 4000) => showNotification(message, "success", duration), + showWarning: (message, duration = 6000) => showNotification(message, "warning", duration), + showInfo: (message, duration = 4000) => showNotification(message, "info", duration), + clearNotification, + }; + + return ( + + {children} + {notification && ( + { + if (reason === "clickaway") { + return; + } + clearNotification(); + }} + anchorOrigin={{ vertical: "bottom", horizontal: "center" }} + > + + {notification.message} + + + )} + + ); +}; + +export const useErrorNotification = () => { + const context = useContext(ErrorNotificationContext); + if (context === undefined) { + throw new Error("useErrorNotification must be used within an ErrorNotificationProvider"); + } + return context; +}; diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/ClaimNftsButton.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/ClaimNftsButton.tsx index 54bc89b84a..e28226119b 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/ClaimNftsButton.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/ClaimNftsButton.tsx @@ -25,10 +25,12 @@ import { useMintTestnetFaucetNfts } from "@api/hooks/useAccounts"; import useAccountStore from "@store/accountStore"; import { substateIdToString } from "@tari-project/typescript-bindings"; import queryClient from "@api/queryClient"; +import { useErrorNotification } from "../../../../contexts/ErrorNotificationContext"; function ClaimNftsButton() { - const { mutate: claimTestnetFaucetNfts } = useMintTestnetFaucetNfts(); + const { mutate: claimTestnetFaucetNfts, isPending } = useMintTestnetFaucetNfts(); const account = useAccountStore((state) => state.account); + const { showError, showSuccess } = useErrorNotification(); if (!account) { return <>; @@ -47,6 +49,7 @@ function ClaimNftsButton() { { onSuccess: (resp: any) => { console.log(resp); + showSuccess("Successfully claimed NFTs!"); // Invalidate NFT queries to refresh the list queryClient.invalidateQueries({ predicate: (query) => { @@ -55,13 +58,23 @@ function ClaimNftsButton() { }, }); }, + onError: (error: any) => { + console.error("Error claiming NFTs:", error); + // Show user-friendly error message + const errorMessage = error?.message || "Failed to claim NFTs. Please ensure you have sufficient funds to pay for transaction fees."; + showError(errorMessage); + }, }, ); }; return ( - ); } From 2f0f734221f91b3beb5c65c734a77eae21a37940 Mon Sep 17 00:00:00 2001 From: Erika Date: Fri, 12 Sep 2025 14:21:18 +0200 Subject: [PATCH 07/10] feat: add placeholder and error handling to tokens --- .../AssetVault/Components/ActionMenu.tsx | 25 ---- .../src/routes/AssetVault/Tokens/Tokens.tsx | 141 +++++++++--------- .../Tokens/components/ClaimCoinsButton.tsx | 79 ++++++++++ 3 files changed, 153 insertions(+), 92 deletions(-) create mode 100644 applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/ClaimCoinsButton.tsx diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx index 3c503b5f85..d8d4d453bd 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx @@ -21,42 +21,20 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import Box from "@mui/material/Box"; -import Button from "@mui/material/Button"; import { useTheme } from "@mui/material/styles"; -import { useAccountsCreateFreeTestCoins } from "@api/hooks/useAccounts"; import ClaimBurn from "./ClaimBurn"; import useAccountStore from "@store/accountStore"; import SendMoney from "../Tokens/components/SendMoney"; import ClaimFees from "./ClaimFees"; import PublishTemplate from "./PublishTemplate"; -import { substateIdToString } from "@tari-project/typescript-bindings"; function ActionMenu() { - const { mutate: claimTestnetFaucetFunds } = useAccountsCreateFreeTestCoins(); const account = useAccountStore((state) => state.account); - const setAccount = useAccountStore((state) => state.setAccount); - const setPublicKey = useAccountStore((state) => state.setPublicKey); const theme = useTheme(); if (!account) { return null; } - const onClaimFreeCoins = () => { - claimTestnetFaucetFunds( - { - account: { ComponentAddress: substateIdToString(account.address) }, - amount: 1_000_000_000, - fee: 1000, - }, - { - onSuccess: (resp) => { - setAccount(resp.account); - setPublicKey(resp.public_key); - }, - }, - ); - }; - return ( - diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx index 7ce8001a66..ea7945167a 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx @@ -26,7 +26,7 @@ import TableCell from "@mui/material/TableCell"; import TableContainer from "@mui/material/TableContainer"; import TableHead from "@mui/material/TableHead"; import TableRow from "@mui/material/TableRow"; -import { Typography, Box } from "@mui/material"; +import { Typography, Box, Stack } from "@mui/material"; import { useState } from "react"; import FetchStatusCheck from "@components/FetchStatusCheck"; import { DataTableCell } from "@components/StyledComponents"; @@ -35,6 +35,7 @@ import useAccountStore from "@store/accountStore"; import { bigintToDecimalString, shortenSubstateId, substateIdToString } from "@utils/helpers"; import { Button } from "@mui/material"; import { SendMoneyDialog } from "./components/SendMoney"; +import ClaimCoinsButton from "./components/ClaimCoinsButton"; import { ResourceAddress, ResourceType, @@ -152,72 +153,78 @@ function Tokens({ account }: { account: Account }) { errorMessage={(balancesError as { message?: string })?.message || "Error fetching data"} isLoading={(balancesIsFetching as boolean) && !balancesData?.balances.length} > - {!hasBalances && !balancesIsFetching ? ( - - - No tokens found - - - This account doesn't have any tokens yet. You can receive tokens by sharing your account address or get testnet tokens from a faucet. - - - ) : ( - - - - - Vault - Resource - Revealed Balance - Confidential Balance - - - - - {balancesData?.balances.map( - ( - { - resource_address, - balance, - resource_type, - confidential_balance, - token_symbol, - vault_address, - divisibility, - }: BalanceEntry, - i: number, - ) => ( - void - } - /> - ), - )} - -
-
- )} + + + + + + {!hasBalances && !balancesIsFetching ? ( + + + No tokens found + + + This account doesn't have any tokens yet. Try claiming some testnet coins to get started. + + + ) : ( + + + + + Vault + Resource + Revealed Balance + Confidential Balance + + + + + {balancesData?.balances.map( + ( + { + resource_address, + balance, + resource_type, + confidential_balance, + token_symbol, + vault_address, + divisibility, + }: BalanceEntry, + i: number, + ) => ( + void + } + /> + ), + )} + +
+
+ )} +
); diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/ClaimCoinsButton.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/ClaimCoinsButton.tsx new file mode 100644 index 0000000000..fa3f5b3b1c --- /dev/null +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/ClaimCoinsButton.tsx @@ -0,0 +1,79 @@ +// Copyright 2025. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import Button from "@mui/material/Button"; +import { useAccountsCreateFreeTestCoins } from "@api/hooks/useAccounts"; +import useAccountStore from "@store/accountStore"; +import { substateIdToString, AccountsCreateFreeTestCoinsResponse } from "@tari-project/typescript-bindings"; +import queryClient from "@api/queryClient"; +import { useErrorNotification } from "@/contexts/ErrorNotificationContext"; + +function ClaimCoinsButton() { + const { mutate: claimTestnetFaucetFunds, isPending } = useAccountsCreateFreeTestCoins(); + const account = useAccountStore((state) => state.account); + const setAccount = useAccountStore((state) => state.setAccount); + const setPublicKey = useAccountStore((state) => state.setPublicKey); + const { showError, showSuccess } = useErrorNotification(); + + if (!account) { + return <>; + } + + const onClaimFreeCoins = () => { + claimTestnetFaucetFunds( + { + account: { ComponentAddress: substateIdToString(account.address) }, + amount: 1_000_000_000, + fee: 1000, + }, + { + onSuccess: (resp: AccountsCreateFreeTestCoinsResponse) => { + setAccount(resp.account); + setPublicKey(resp.public_key); + showSuccess("Successfully claimed testnet coins!"); + queryClient.invalidateQueries({ + predicate: (query) => { + const key = query.queryKey[0]; + return ( + typeof key === "string" && + (key === "balances" || key === "accounts_balances" || key.startsWith("accounts_get_balances")) + ); + }, + }); + }, + onError: (error: any) => { + console.error("Error claiming coins:", error); + const errorMessage = error?.message || "Failed to claim testnet coins. Please try again."; + showError(errorMessage); + }, + }, + ); + }; + + return ( + + ); +} + +export default ClaimCoinsButton; From 26eb2437bdd23cd5543bdf369a1fc2544857f113 Mon Sep 17 00:00:00 2001 From: Erika Date: Fri, 12 Sep 2025 16:47:18 +0200 Subject: [PATCH 08/10] feat: improve error notifications --- .../web_ui/src/contexts/ErrorNotificationContext.tsx | 9 ++++++++- .../web_ui/src/routes/AssetVault/Tokens/Tokens.tsx | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/applications/tari_walletd/web_ui/src/contexts/ErrorNotificationContext.tsx b/applications/tari_walletd/web_ui/src/contexts/ErrorNotificationContext.tsx index ac16b02e17..e7e8a699b5 100644 --- a/applications/tari_walletd/web_ui/src/contexts/ErrorNotificationContext.tsx +++ b/applications/tari_walletd/web_ui/src/contexts/ErrorNotificationContext.tsx @@ -85,7 +85,14 @@ export const ErrorNotificationProvider: React.FC<{ children: React.ReactNode }> }} anchorOrigin={{ vertical: "bottom", horizontal: "center" }} > - + {notification.message} diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx index ea7945167a..470cfbc050 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/Tokens.tsx @@ -151,14 +151,14 @@ function Tokens({ account }: { account: Account }) { - {!hasBalances && !balancesIsFetching ? ( + {balancesData && !hasBalances ? ( Date: Mon, 15 Sep 2025 10:53:56 +0200 Subject: [PATCH 09/10] fix: assets refresh button --- .../routes/AssetVault/Components/MyAssets.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/MyAssets.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/MyAssets.tsx index d0d1bf92ed..909922d5f6 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/MyAssets.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/MyAssets.tsx @@ -24,6 +24,7 @@ import Grid from "@mui/material/Grid"; import Box from "@mui/material/Box"; import Divider from "@mui/material/Divider"; import Typography from "@mui/material/Typography"; +import IconButton from "@mui/material/IconButton"; import { useTheme } from "@mui/material/styles"; import { InnerHeading, StyledPaper } from "@components/StyledComponents"; import { refreshAccountsBalances } from "@api/hooks/useAccounts"; @@ -35,8 +36,8 @@ import ActionMenu from "./ActionMenu"; import Assets from "./Assets"; import SelectAccount from "./SelectAccount"; import { substateIdToString } from "@tari-project/typescript-bindings"; -import { Button } from "@mui/material"; import { Refresh } from "@mui/icons-material"; +import queryClient from "@api/queryClient"; function MyAssets() { const theme = useTheme(); @@ -49,6 +50,12 @@ function MyAssets() { const refreshBalances = refreshAccountsBalances(substateIdToString(account.address)); const handleRefreshClicked = () => { refreshBalances.mutate(); + queryClient.invalidateQueries({ + predicate: (query) => { + const key = query.queryKey[0]; + return typeof key === "string" && (key === "nfts" || key === "list_nfts" || key === "nfts_list"); + }, + }); }; return ( @@ -112,14 +119,18 @@ function MyAssets() { Assets - + From 5b7499f45f8743f684357da25daec636cfdc4740 Mon Sep 17 00:00:00 2001 From: Erika Date: Mon, 15 Sep 2025 15:24:19 +0200 Subject: [PATCH 10/10] feat: improve add account and popup titles --- .../web_ui/src/Components/PopupTitle.tsx | 57 ++++++++ .../AssetVault/Components/ActionMenu.tsx | 16 +-- .../AssetVault/Components/AddAccount.tsx | 135 ++++++++++++------ .../AssetVault/Components/ClaimBurn.tsx | 9 +- .../AssetVault/Components/ClaimFees.tsx | 6 +- .../AssetVault/Components/PublishTemplate.tsx | 22 +-- .../AssetVault/Components/SelectAccount.tsx | 6 +- .../AssetVault/NFTs/components/SendNft.tsx | 18 +-- .../Tokens/components/SendMoney.tsx | 21 +-- 9 files changed, 181 insertions(+), 109 deletions(-) create mode 100644 applications/tari_walletd/web_ui/src/Components/PopupTitle.tsx diff --git a/applications/tari_walletd/web_ui/src/Components/PopupTitle.tsx b/applications/tari_walletd/web_ui/src/Components/PopupTitle.tsx new file mode 100644 index 0000000000..46b0561106 --- /dev/null +++ b/applications/tari_walletd/web_ui/src/Components/PopupTitle.tsx @@ -0,0 +1,57 @@ +// Copyright 2025. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import { DialogTitle, IconButton, Stack, Typography, Divider } from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; + +interface PopupTitleProps { + title: string; + onClose?: () => void; +} + +function PopupTitle({ title, onClose }: PopupTitleProps) { + return ( + + + + + {title} + + + {onClose && ( + + + + )} + + + + ); +} + +export default PopupTitle; diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx index d8d4d453bd..f420c1738c 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx @@ -20,34 +20,24 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import Box from "@mui/material/Box"; -import { useTheme } from "@mui/material/styles"; +import Stack from "@mui/material/Stack"; import ClaimBurn from "./ClaimBurn"; import useAccountStore from "@store/accountStore"; -import SendMoney from "../Tokens/components/SendMoney"; import ClaimFees from "./ClaimFees"; import PublishTemplate from "./PublishTemplate"; function ActionMenu() { const account = useAccountStore((state) => state.account); - const theme = useTheme(); if (!account) { return null; } return ( - - + - + ); } diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AddAccount.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AddAccount.tsx index 8b8183fa96..348b9cde08 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AddAccount.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/AddAccount.tsx @@ -26,41 +26,44 @@ import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; import Dialog from "@mui/material/Dialog"; import DialogContent from "@mui/material/DialogContent"; -import DialogTitle from "@mui/material/DialogTitle"; -import Box from "@mui/material/Box"; -import Snackbar from "@mui/material/Snackbar"; +import { CircularProgress, Alert } from "@mui/material"; import { useAccountsCreate } from "@api/hooks/useAccounts"; +import useAccountStore from "@store/accountStore"; +import type { AccountsCreateResponse } from "@tari-project/typescript-bindings"; import { useTheme } from "@mui/material/styles"; import queryClient from "@api/queryClient"; +import { Stack, Fade, Typography } from "@mui/material"; +import CheckCircleRoundedIcon from "@mui/icons-material/CheckCircleRounded"; +import PopupTitle from "@/components/PopupTitle"; function AddAccount({ open, setOpen }: { open: boolean; setOpen: React.Dispatch> }) { const [accountFormState, setAccountFormState] = useState({ accountName: "", }); - const { mutateAsync: mutateAddAccount } = useAccountsCreate(); + const { mutateAsync: mutateAddAccount, isPending, error, isSuccess, data, reset } = useAccountsCreate(); const theme = useTheme(); - const [isBusy, setIsBusy] = useState(false); + const setAccount = useAccountStore((state) => state.setAccount); + const setPublicKey = useAccountStore((state) => state.setPublicKey); const handleClose = () => { + setAccountFormState({ accountName: "" }); + reset(); setOpen(false); }; const onSubmitAddAccount = async (e: FormEvent) => { e.preventDefault(); - setIsBusy(true); - await mutateAddAccount( - { accountName: accountFormState.accountName }, - { - onSettled: () => { - setAccountFormState({ - accountName: "", - }); - setOpen(false); - queryClient.invalidateQueries({ queryKey: ["accounts"] }); - }, - }, - ); - setIsBusy(false); + try { + const newAccount: AccountsCreateResponse = await mutateAddAccount({ accountName: accountFormState.accountName }); + setAccount(newAccount.account); + setPublicKey(newAccount.public_key); + queryClient.invalidateQueries({ queryKey: ["accounts"] }); + setTimeout(() => { + handleClose(); + }, 3000); + } catch (error) { + console.error("Failed to create account:", error); + } }; const onAccountChange = (e: React.ChangeEvent) => { @@ -70,37 +73,77 @@ function AddAccount({ open, setOpen }: { open: boolean; setOpen: React.Dispatch< }); }; + const getErrorMessage = (error: any): string => { + if (!error) return ""; + const message = error.message || ""; + const invalidRequestMatch = message.match(/Invalid request:\s*(.+)/); + if (invalidRequestMatch) { + return invalidRequestMatch[1]; + } + return message || "Failed to create account. Please try again."; + }; + return ( - Add Account - -
- - + {!isSuccess ? ( + + + + + + + + + + + ) : ( + + - - - - -
+ + + + Account created successfully! + + + )}
); } diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ClaimBurn.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ClaimBurn.tsx index 3d21527744..ffccf4130b 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ClaimBurn.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ClaimBurn.tsx @@ -26,7 +26,6 @@ import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; import Dialog from "@mui/material/Dialog"; import DialogContent from "@mui/material/DialogContent"; -import DialogTitle from "@mui/material/DialogTitle"; import FormControl from "@mui/material/FormControl"; import InputLabel from "@mui/material/InputLabel"; import Select, { SelectChangeEvent } from "@mui/material/Select/Select"; @@ -36,8 +35,8 @@ import { useAccountsList } from "@api/hooks/useAccounts"; import { useTheme } from "@mui/material/styles"; import { accountsClaimBurn, transactionsWaitResult } from "@utils/json_rpc"; import useAccountStore from "@store/accountStore"; -import { useKeysList } from "@api/hooks/useKeys"; -import type { AccountInfo, ComponentAddress } from "@tari-project/typescript-bindings"; +import type { ComponentAddress, AccountInfo } from "@tari-project/typescript-bindings"; +import PopupTitle from "@/components/PopupTitle"; type FormState = { account: ComponentAddress; @@ -139,7 +138,7 @@ export default function ClaimBurn() { Claim Burn - Claim Burn +
@@ -153,7 +152,7 @@ export default function ClaimBurn() { style={{ flexGrow: 1, minWidth: "200px" }} disabled={claimBurnFormState.disabled} > - {accountsList?.accounts?.map((account, i) => ( + {accountsList?.accounts?.map((account: AccountInfo, i: number) => (
diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ClaimFees.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ClaimFees.tsx index 27a6200ea6..b7e23cdadf 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ClaimFees.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ClaimFees.tsx @@ -26,7 +26,6 @@ import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; import Dialog from "@mui/material/Dialog"; import DialogContent from "@mui/material/DialogContent"; -import DialogTitle from "@mui/material/DialogTitle"; import Box from "@mui/material/Box"; import { useAccountsList } from "@api/hooks/useAccounts"; import { useTheme } from "@mui/material/styles"; @@ -42,8 +41,7 @@ import { substateIdToString, TransactionResult, } from "@tari-project/typescript-bindings"; -import { FileContent } from "use-file-picker/types"; -import { toHexString } from "@utils/helpers"; +import PopupTitle from "@/components/PopupTitle"; interface FormState { account: string | null; @@ -214,7 +212,7 @@ export default function ClaimFees() { Claim Fees - Claim Fees + diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/PublishTemplate.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/PublishTemplate.tsx index 73d9b2a5e4..228d2fba29 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/PublishTemplate.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/PublishTemplate.tsx @@ -26,7 +26,6 @@ import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; import Dialog from "@mui/material/Dialog"; import DialogContent from "@mui/material/DialogContent"; -import DialogTitle from "@mui/material/DialogTitle"; import Box from "@mui/material/Box"; import { useAccountsList } from "@api/hooks/useAccounts"; import { useTheme } from "@mui/material/styles"; @@ -35,12 +34,19 @@ import Select from "@mui/material/Select"; import { SelectChangeEvent } from "@mui/material/Select/Select"; import MenuItem from "@mui/material/MenuItem"; import { useFilePicker } from "use-file-picker"; -import { ResourceAddress, ResourceType, substateIdToString } from "@tari-project/typescript-bindings"; +import { + ResourceAddress, + ResourceType, + substateIdToString, + PublishTemplateResponse, + AccountInfo, +} from "@tari-project/typescript-bindings"; import InputLabel from "@mui/material/InputLabel"; import { usePublishTemplate } from "@api/hooks/useTransactions"; import { FileAmountLimitValidator, FileSizeValidator, FileTypeValidator } from "use-file-picker/validators"; import { FileContent } from "use-file-picker/types"; import { base64FromArrayBuffer } from "@utils/helpers"; +import PopupTitle from "@/components/PopupTitle"; export default function PublishTemplate() { const [open, setOpen] = useState(false); @@ -135,7 +141,7 @@ function PublishTemplateDialog(props: DialogProps) { detect_inputs: true, dry_run: isDryRun, }) - .then((resp) => { + .then((resp: PublishTemplateResponse) => { if (isDryRun) { setFormState({ ...formState, maxFee: resp.dry_run_fee! }); } else { @@ -144,7 +150,7 @@ function PublishTemplateDialog(props: DialogProps) { setPopup({ title: "Publish template transaction submitted", error: false }); } }) - .catch((e) => { + .catch((e: Error) => { setPopup({ title: "Publish failed", error: true, message: e.message }); }) .finally(() => { @@ -198,7 +204,7 @@ function PublishTemplateDialog(props: DialogProps) { }); useEffect(() => { - let account = accounts?.find((a) => a.account.is_default)?.account.name || null; + let account = accounts?.find((a: AccountInfo) => a.account.is_default)?.account.name || null; if (account) { setFormState({ ...INITIAL_VALUES, account }); setValidity({ ...validity, account: true }); @@ -207,7 +213,7 @@ function PublishTemplateDialog(props: DialogProps) { return ( - Publish Template + {accounts && ( @@ -218,11 +224,11 @@ function PublishTemplateDialog(props: DialogProps) { name="account" disabled={disabled} displayEmpty - value={formState.account || accounts.find((a) => a.account.is_default) || ""} + value={formState.account || accounts.find((a: AccountInfo) => a.account.is_default) || ""} onChange={setSelectFormValue} variant="outlined" > - {accounts.map((account, i) => ( + {accounts.map((account: AccountInfo, i: number) => ( {account.account.name} {account.account.is_default ? "(default)" : ""} diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/SelectAccount.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/SelectAccount.tsx index 4c2782625b..8c160211db 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/SelectAccount.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/SelectAccount.tsx @@ -68,8 +68,10 @@ function SelectAccount() { labelId="account-select-label" id="account-select" value={ - dataAccountsList?.accounts.some((info: AccountInfo) => info.account.address === account?.address) - ? substateIdToString(account!.address) + account && dataAccountsList?.accounts.some((info: AccountInfo) => info.account.address === account?.address) + ? substateIdToString(account.address) + : dataAccountsList?.accounts.length + ? substateIdToString(dataAccountsList.accounts[0].account.address) : "addAccount" } label="Account" diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/SendNft.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/SendNft.tsx index ee78c06599..1c9ede534f 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/SendNft.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/NFTs/components/SendNft.tsx @@ -24,8 +24,6 @@ import { FormEvent, useEffect, useState, useMemo } from "react"; import Button from "@mui/material/Button"; import Dialog from "@mui/material/Dialog"; import DialogContent from "@mui/material/DialogContent"; -import DialogTitle from "@mui/material/DialogTitle"; -import { Stepper, Step, StepLabel } from "@mui/material"; import { SelectChangeEvent } from "@mui/material/Select/Select"; import useAccountStore from "@store/accountStore"; import type { @@ -42,6 +40,7 @@ import { useNftTransferStore } from "@store/nftTransferStore"; import FormStep from "../steps/FormStep"; import ConfirmationStep from "../steps/ConfirmationStep"; import ResultStep from "../steps/ResultStep"; +import PopupTitle from "@/components/PopupTitle"; interface TransferNftProps { account?: Account; @@ -132,7 +131,7 @@ export function TransferNftDialog(props: TransferNftDialogProps) { const [isEstimatingFee, setLocalIsEstimatingFee] = useState(false); // Memoize account selectors to prevent infinite re-renders - now nullable - const sourceAccount = useMemo(() => account ? getAccountSelector(account) : null, [account]); + const sourceAccount = useMemo(() => (account ? getAccountSelector(account) : null), [account]); const feePayerAccount = useMemo(() => { if (!sourceAccount) return null; return transferFormState.payerAccount ? { ComponentAddress: transferFormState.payerAccount } : sourceAccount; @@ -392,19 +391,10 @@ export function TransferNftDialog(props: TransferNftDialogProps) { return ( - - Transfer NFT - - {steps.map((label) => ( - - {label} - - ))} - - + {!account ? ( -
+

Please select an account first to transfer NFTs.

) : ( diff --git a/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx index ef83c6fef2..c888d573af 100644 --- a/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx +++ b/applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx @@ -24,9 +24,6 @@ import { FormEvent, useState, useEffect } from "react"; import Button from "@mui/material/Button"; import Dialog from "@mui/material/Dialog"; import DialogContent from "@mui/material/DialogContent"; -import DialogTitle from "@mui/material/DialogTitle"; -import IconButton from "@mui/material/IconButton"; -import CloseIcon from "@mui/icons-material/Close"; import { useAccountsGetBalances, useAccountsTransfer } from "@api/hooks/useAccounts"; import useAccountStore from "@store/accountStore"; import { SelectChangeEvent } from "@mui/material/Select/Select"; @@ -42,14 +39,14 @@ import { transactionsWaitResult } from "@utils/json_rpc"; import FormStep, { SendMoneyFormState } from "../steps/FormStep"; import ConfirmationStep from "../steps/ConfirmationStep"; import ResultStep, { TransferResult } from "../steps/ResultStep"; -import { Divider, Stack, Typography } from "@mui/material"; +import PopupTitle from "@/components/PopupTitle"; export default function SendMoney() { const [open, setOpen] = useState(false); const { account } = useAccountStore(); const { data } = useAccountsGetBalances(account ? substateIdToString(account.address) : ""); - const xtrBalanceEntry = data?.balances?.find((b) => b.resource_address === XTR); + const xtrBalanceEntry = data?.balances?.find((b: BalanceEntry) => b.resource_address === XTR); return ( <> @@ -372,18 +369,8 @@ export function SendMoneyDialog(props: SendMoneyDialogProps) { return ( - - - Send Tari - - - - - - - - {renderStepContent()} - + + {renderStepContent()} ); }