diff --git a/applications/tari_walletd/web_ui/src/App.tsx b/applications/tari_walletd/web_ui/src/App.tsx
index 002a54d16e..0c2384ff5e 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";
@@ -42,6 +42,8 @@ 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";
+import { ErrorNotificationProvider } from "./contexts/ErrorNotificationContext";
export const breadcrumbRoutes = [
{
@@ -160,6 +162,8 @@ function App() {
const { authToken } = authStore;
let isAuthenticated = !!authToken;
+ useCurrencySync();
+
useEffect(() => {
if (isTokenExpired(authToken) && authToken !== AUTH_TOKEN_FOR_NONE_AUTH) {
authStore.clearToken();
@@ -193,10 +197,11 @@ function App() {
}, [authMethod, authMethodsIsError]);
return (
-
-
- }>
- } />
+
+
+
+ }>
+ } />
} />
} />
+
);
}
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/contexts/ErrorNotificationContext.tsx b/applications/tari_walletd/web_ui/src/contexts/ErrorNotificationContext.tsx
new file mode 100644
index 0000000000..e7e8a699b5
--- /dev/null
+++ b/applications/tari_walletd/web_ui/src/contexts/ErrorNotificationContext.tsx
@@ -0,0 +1,110 @@
+// 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/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/AssetVault/Components/ActionMenu.tsx b/applications/tari_walletd/web_ui/src/routes/AssetVault/Components/ActionMenu.tsx
index 3c503b5f85..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,59 +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 Button from "@mui/material/Button";
-import { useTheme } from "@mui/material/styles";
-import { useAccountsCreateFreeTestCoins } from "@api/hooks/useAccounts";
+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";
-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/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 (
);
}
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