diff --git a/src/components/pure-status-action-bar.tsx b/src/components/pure-status-action-bar.tsx index 1a527e82c..ed90e79f5 100644 --- a/src/components/pure-status-action-bar.tsx +++ b/src/components/pure-status-action-bar.tsx @@ -29,7 +29,6 @@ import thumbUpIcon from '@tabler/icons/outline/thumb-up.svg'; import trashIcon from '@tabler/icons/outline/trash.svg'; import uploadIcon from '@tabler/icons/outline/upload.svg'; import volume3Icon from '@tabler/icons/outline/volume-3.svg'; -import { useEffect } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { useHistory, useRouteMatch } from 'react-router-dom'; @@ -49,8 +48,7 @@ import DropdownMenu from 'soapbox/components/dropdown-menu/index.ts'; import PureStatusReactionWrapper from 'soapbox/components/pure-status-reaction-wrapper.tsx'; import StatusActionButton from 'soapbox/components/status-action-button.tsx'; import HStack from 'soapbox/components/ui/hstack.tsx'; -import { useCashu } from 'soapbox/features/zap/hooks/useCashu.ts'; -import { useApi } from 'soapbox/hooks/useApi.ts'; +import { useNutzapRequest, useWallet } from 'soapbox/features/zap/hooks/useHooks.ts'; import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts'; import { useDislike } from 'soapbox/hooks/useDislike.ts'; @@ -166,7 +164,6 @@ const PureStatusActionBar: React.FC = ({ statusActionButtonTheme = 'default', }) => { const intl = useIntl(); - const api = useApi(); // TODO: Remove this part after patrick implement in backend const history = useHistory(); const dispatch = useAppDispatch(); const match = useRouteMatch<{ groupSlug: string }>('/group/:groupSlug'); @@ -183,8 +180,9 @@ const PureStatusActionBar: React.FC = ({ const features = useFeatures(); const { boostModal, deleteModal } = useSettings(); - const { wallet, getWallet, nutzapsList } = useCashu(); - const isNutzapped = Object.keys(nutzapsList).some((nutzap)=> nutzap === status.id); // TODO: Remove "getWallet" to + const { wallet } = useWallet(); + const { nutzapsList } = useNutzapRequest(); + const isNutzapped = Object.keys(nutzapsList).some((nutzap)=> nutzap === status.id); // TODO: Remove "getWallet" after been in backend const { account } = useOwnAccount(); const isStaff = account ? account.staff : false; @@ -200,12 +198,6 @@ const PureStatusActionBar: React.FC = ({ const { unpinFromGroup, pinToGroup } = usePinGroup(); const { initReport } = useInitReport(); - useEffect( - () => { - getWallet(api, false); - } // TODO: Remove - , []); - if (!status) { return null; } diff --git a/src/components/status-action-bar.tsx b/src/components/status-action-bar.tsx index 0d5ef1181..ff72a3664 100644 --- a/src/components/status-action-bar.tsx +++ b/src/components/status-action-bar.tsx @@ -29,7 +29,6 @@ import thumbUpIcon from '@tabler/icons/outline/thumb-up.svg'; import trashIcon from '@tabler/icons/outline/trash.svg'; import uploadIcon from '@tabler/icons/outline/upload.svg'; import volume3Icon from '@tabler/icons/outline/volume-3.svg'; -import { useEffect } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { useHistory, useRouteMatch } from 'react-router-dom'; @@ -50,8 +49,7 @@ import DropdownMenu from 'soapbox/components/dropdown-menu/index.ts'; import StatusActionButton from 'soapbox/components/status-action-button.tsx'; import StatusReactionWrapper from 'soapbox/components/status-reaction-wrapper.tsx'; import HStack from 'soapbox/components/ui/hstack.tsx'; -import { useCashu } from 'soapbox/features/zap/hooks/useCashu.ts'; -import { useApi } from 'soapbox/hooks/useApi.ts'; +import { useNutzapRequest, useWallet } from 'soapbox/features/zap/hooks/useHooks.ts'; import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts'; import { useFeatures } from 'soapbox/hooks/useFeatures.ts'; @@ -162,7 +160,6 @@ const StatusActionBar: React.FC = ({ }) => { const intl = useIntl(); const history = useHistory(); - const api = useApi(); // TODO: Remove this part after patrick implement in backend const dispatch = useAppDispatch(); const match = useRouteMatch<{ groupSlug: string }>('/group/:groupSlug'); @@ -178,7 +175,8 @@ const StatusActionBar: React.FC = ({ const features = useFeatures(); const { boostModal, deleteModal } = useSettings(); - const { wallet, getWallet, nutzapsList } = useCashu(); + const { wallet } = useWallet(); + const { nutzapsList } = useNutzapRequest(); const isNutzapped = Object.keys(nutzapsList).some((nutzap)=> nutzap === status.id); const { account } = useOwnAccount(); @@ -188,12 +186,6 @@ const StatusActionBar: React.FC = ({ const { toggleReblog } = useReblog(); const { bookmark, unbookmark } = useBookmark(); - useEffect( - () => { - getWallet(api, false); - } // TODO: remove - , []); - if (!status) { return null; } diff --git a/src/features/account/components/header.tsx b/src/features/account/components/header.tsx index 355328517..c909f8f86 100644 --- a/src/features/account/components/header.tsx +++ b/src/features/account/components/header.tsx @@ -21,7 +21,6 @@ import userIcon from '@tabler/icons/outline/user.svg'; import { useMutation } from '@tanstack/react-query'; import { List as ImmutableList } from 'immutable'; import { nip19 } from 'nostr-tools'; -import { useEffect } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; @@ -45,9 +44,8 @@ import VerificationBadge from 'soapbox/components/verification-badge.tsx'; import MovedNote from 'soapbox/features/account-timeline/components/moved-note.tsx'; import ActionButton from 'soapbox/features/ui/components/action-button.tsx'; import SubscriptionButton from 'soapbox/features/ui/components/subscription-button.tsx'; -import { useCashu } from 'soapbox/features/zap/hooks/useCashu.ts'; +import { useWallet } from 'soapbox/features/zap/hooks/useHooks.ts'; import { usePaymentMethod } from 'soapbox/features/zap/usePaymentMethod.ts'; -import { useApi } from 'soapbox/hooks/useApi.ts'; import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts'; import { useFeatures } from 'soapbox/hooks/useFeatures.ts'; @@ -113,7 +111,6 @@ interface IHeader { const Header: React.FC = ({ account }) => { const intl = useIntl(); const history = useHistory(); - const api = useApi(); // TODO: Remove this part after patrick implement in backend const dispatch = useAppDispatch(); const features = useFeatures(); @@ -124,7 +121,7 @@ const Header: React.FC = ({ account }) => { const { software } = useAppSelector((state) => parseVersion(state.instance.version)); - const { wallet, getWallet } = useCashu(); + const { wallet } = useWallet(); const { getOrCreateChatByAccountId } = useChats(); @@ -145,13 +142,6 @@ const Header: React.FC = ({ account }) => { }, }); - - useEffect( - () => { - getWallet(api, false); - } // TODO: remove - , []); - if (!account) { return (
diff --git a/src/features/wallet/components/balance.tsx b/src/features/wallet/components/balance.tsx index 85a2d9690..dd14d883c 100644 --- a/src/features/wallet/components/balance.tsx +++ b/src/features/wallet/components/balance.tsx @@ -19,7 +19,7 @@ import Input from 'soapbox/components/ui/input.tsx'; import Stack from 'soapbox/components/ui/stack.tsx'; import Text from 'soapbox/components/ui/text.tsx'; import { SelectDropdown } from 'soapbox/features/forms/index.tsx'; -import { useCashu } from 'soapbox/features/zap/hooks/useCashu.ts'; +import { useWallet } from 'soapbox/features/zap/hooks/useHooks.ts'; import { useApi } from 'soapbox/hooks/useApi.ts'; import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts'; import { Quote, quoteSchema } from 'soapbox/schemas/wallet.ts'; @@ -90,7 +90,7 @@ const NewMint = ({ onBack, list }: NewMintProps) => { const [currentState, setCurrentState] = useState<'loading' | 'paid' | 'default'>('default'); const api = useApi(); const intl = useIntl(); - const { getWallet } = useCashu(); + const { getWallet } = useWallet(); const now = Math.floor(Date.now() / 1000); @@ -112,7 +112,7 @@ const NewMint = ({ onBack, list }: NewMintProps) => { toast.success(intl.formatMessage(messages.paidMessage)); onBack(); // onChange(); - getWallet(api); + getWallet(); // TODO: Remove handleClean(); setCurrentState('default'); } @@ -236,8 +236,7 @@ const NewMint = ({ onBack, list }: NewMintProps) => { }; const Balance = () => { - const api = useApi(); - const { wallet, getWallet } = useCashu(); + const { wallet } = useWallet(); const [amount, setAmount] = useState(0); const [mints, setMints] = useState([]); const { account } = useOwnAccount(); @@ -248,10 +247,6 @@ const Balance = () => { newMint: setCurrent('balance')} list={mints} />, }; - useEffect(() => { - getWallet(api); - }, []); - useEffect( () => { if (wallet){ diff --git a/src/features/wallet/components/create-wallet.tsx b/src/features/wallet/components/create-wallet.tsx index e8088041b..7c1a51e1a 100644 --- a/src/features/wallet/components/create-wallet.tsx +++ b/src/features/wallet/components/create-wallet.tsx @@ -11,8 +11,7 @@ import Stack from 'soapbox/components/ui/stack.tsx'; import SvgIcon from 'soapbox/components/ui/svg-icon.tsx'; import Text from 'soapbox/components/ui/text.tsx'; import { MintEditor } from 'soapbox/features/wallet/components/editable-lists.tsx'; -import { useCashu } from 'soapbox/features/zap/hooks/useCashu.ts'; -import { useApi } from 'soapbox/hooks/useApi.ts'; +import { useWallet } from 'soapbox/features/zap/hooks/useHooks.ts'; import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts'; const messages = defineMessages({ @@ -23,13 +22,12 @@ const messages = defineMessages({ }); const CreateWallet = () => { - const api = useApi(); const intl = useIntl(); const { account } = useOwnAccount(); const [formActive, setFormActive] = useState(false); const [isLoading, setIsLoading] = useState(false); const [mints, setMints] = useState([]); - const { createWallet } = useCashu(); + const { createWallet } = useWallet(); const handleSubmit = async () => { setIsLoading(true); @@ -39,7 +37,7 @@ const CreateWallet = () => { relays: [], }; - await createWallet(api, walletInfo); + await createWallet(walletInfo); setIsLoading(false); }; diff --git a/src/features/wallet/components/transactions.tsx b/src/features/wallet/components/transactions.tsx index ca29aee84..9b50ab9f5 100644 --- a/src/features/wallet/components/transactions.tsx +++ b/src/features/wallet/components/transactions.tsx @@ -2,7 +2,6 @@ import arrowBarDownIcon from '@tabler/icons/outline/arrow-bar-down.svg'; import arrowBarUpIcon from '@tabler/icons/outline/arrow-bar-up.svg'; import moreIcon from '@tabler/icons/outline/dots-circle-horizontal.svg'; import questionIcon from '@tabler/icons/outline/question-mark.svg'; -import { useEffect } from 'react'; import { FormattedDate, defineMessages, useIntl } from 'react-intl'; import Button from 'soapbox/components/ui/button.tsx'; @@ -12,8 +11,7 @@ import Spinner from 'soapbox/components/ui/spinner.tsx'; import Stack from 'soapbox/components/ui/stack.tsx'; import SvgIcon from 'soapbox/components/ui/svg-icon.tsx'; import Text from 'soapbox/components/ui/text.tsx'; -import { useCashu } from 'soapbox/features/zap/hooks/useCashu.ts'; -import { useApi } from 'soapbox/hooks/useApi.ts'; +import { useTransactions } from 'soapbox/features/zap/hooks/useHooks.ts'; import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts'; const themes = { @@ -95,15 +93,8 @@ const TransactionItem = (e: { amount: number; created_at: number ; direction: ' const Transactions = () => { const intl = useIntl(); - const api = useApi(); const { account } = useOwnAccount(); - const { transactions, getTransactions } = useCashu(); - - useEffect( - () => { - getTransactions(api); - } - , []); + const { transactions } = useTransactions(); if (!account) { return null; diff --git a/src/features/wallet/components/wallet-mints.tsx b/src/features/wallet/components/wallet-mints.tsx index 8521dd771..c9ca48c5b 100644 --- a/src/features/wallet/components/wallet-mints.tsx +++ b/src/features/wallet/components/wallet-mints.tsx @@ -5,7 +5,7 @@ import Button from 'soapbox/components/ui/button.tsx'; import { Column } from 'soapbox/components/ui/column.tsx'; import Stack from 'soapbox/components/ui/stack.tsx'; import { MintEditor } from 'soapbox/features/wallet/components/editable-lists.tsx'; -import { useCashu } from 'soapbox/features/zap/hooks/useCashu.ts'; +import { useWallet } from 'soapbox/features/zap/hooks/useHooks.ts'; import { useApi } from 'soapbox/hooks/useApi.ts'; import toast from 'soapbox/toast.tsx'; import { isURL } from 'soapbox/utils/auth.ts'; @@ -23,7 +23,7 @@ const messages = defineMessages({ const WalletMints = () => { const intl = useIntl(); const api = useApi(); - const { wallet, getWallet } = useCashu(); + const { wallet } = useWallet(); const [relays, setRelays] = useState([]); const [initialMints, setInitialMints] = useState([]); @@ -56,10 +56,6 @@ const WalletMints = () => { } }; - useEffect(() => { - getWallet(api, false); - }, []); - useEffect( () => { if (wallet) { diff --git a/src/features/wallet/components/wallet-relays.tsx b/src/features/wallet/components/wallet-relays.tsx index cd8685c21..74c4bcf79 100644 --- a/src/features/wallet/components/wallet-relays.tsx +++ b/src/features/wallet/components/wallet-relays.tsx @@ -5,7 +5,7 @@ import Button from 'soapbox/components/ui/button.tsx'; import { Column } from 'soapbox/components/ui/column.tsx'; import Stack from 'soapbox/components/ui/stack.tsx'; import { RelayEditor } from 'soapbox/features/wallet/components/editable-lists.tsx'; -import { useCashu } from 'soapbox/features/zap/hooks/useCashu.ts'; +import { useWallet } from 'soapbox/features/zap/hooks/useHooks.ts'; import { useApi } from 'soapbox/hooks/useApi.ts'; import toast from 'soapbox/toast.tsx'; import { isURL } from 'soapbox/utils/auth.ts'; @@ -23,7 +23,7 @@ const messages = defineMessages({ const WalletRelays = () => { const intl = useIntl(); const api = useApi(); - const { wallet, getWallet } = useCashu(); + const { wallet } = useWallet(); const [relays, setRelays] = useState([]); const [initialRelays, setInitialRelays] = useState([]); @@ -55,10 +55,6 @@ const WalletRelays = () => { } }; - useEffect(() => { - getWallet(api, false); - }, []); - useEffect( () => { if (wallet) { diff --git a/src/features/wallet/index.tsx b/src/features/wallet/index.tsx index 7f972d3d5..b61ae56b5 100644 --- a/src/features/wallet/index.tsx +++ b/src/features/wallet/index.tsx @@ -1,4 +1,3 @@ -import { useEffect, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import List, { ListItem } from 'soapbox/components/list.tsx'; @@ -10,15 +9,14 @@ import { SelectDropdown } from 'soapbox/features/forms/index.tsx'; import Balance from 'soapbox/features/wallet/components/balance.tsx'; import CreateWallet from 'soapbox/features/wallet/components/create-wallet.tsx'; import Transactions from 'soapbox/features/wallet/components/transactions.tsx'; -import { useCashu } from 'soapbox/features/zap/hooks/useCashu.ts'; +import { useWallet } from 'soapbox/features/zap/hooks/useHooks.ts'; import { usePaymentMethod } from 'soapbox/features/zap/usePaymentMethod.ts'; -import { useApi } from 'soapbox/hooks/useApi.ts'; import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts'; const messages = defineMessages({ payment: { id: 'wallet.payment', defaultMessage: 'Payment Method' }, - relays: { id: 'wallet.relays', defaultMessage: 'Relays' }, + relays: { id: 'wallet.relays', defaultMessage: 'Wallet Relays' }, transactions: { id: 'wallet.transactions', defaultMessage: 'Transactions' }, wallet: { id: 'wallet', defaultMessage: 'Wallet' }, management: { id: 'wallet.management', defaultMessage: 'Wallet Management' }, @@ -32,19 +30,12 @@ const paymentMethods = { /** User Wallet page. */ const Wallet = () => { - const api = useApi(); const intl = useIntl(); const { account } = useOwnAccount(); - const { wallet: walletData, getWallet } = useCashu(); - const [isLoading, setIsLoading] = useState(true); + const { wallet: walletData, isLoading } = useWallet(); const { method, changeMethod } = usePaymentMethod(); - useEffect(() => { - getWallet(api); - setIsLoading(false); - }, []); - if (!account) return null; return ( diff --git a/src/features/zap/components/pay-request-form.tsx b/src/features/zap/components/pay-request-form.tsx index d57d2fddf..f05f4551b 100644 --- a/src/features/zap/components/pay-request-form.tsx +++ b/src/features/zap/components/pay-request-form.tsx @@ -21,9 +21,8 @@ import Input from 'soapbox/components/ui/input.tsx'; import Stack from 'soapbox/components/ui/stack.tsx'; import SvgIcon from 'soapbox/components/ui/svg-icon.tsx'; import Text from 'soapbox/components/ui/text.tsx'; -import { useCashu } from 'soapbox/features/zap/hooks/useCashu.ts'; +import { useNutzapRequest } from 'soapbox/features/zap/hooks/useHooks.ts'; import { usePaymentMethod } from 'soapbox/features/zap/usePaymentMethod.ts'; -import { useApi } from 'soapbox/hooks/useApi.ts'; import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; import { emojifyText } from 'soapbox/utils/emojify.tsx'; import { capitalize } from 'soapbox/utils/strings.ts'; @@ -57,7 +56,6 @@ const messages = defineMessages({ const PayRequestForm = ({ account, status, onClose }: IPayRequestForm) => { const intl = useIntl(); - const api = useApi(); const dispatch = useAppDispatch(); const [zapComment, setZapComment] = useState(''); const [amount, setAmount] = useState(50); @@ -66,7 +64,7 @@ const PayRequestForm = ({ account, status, onClose }: IPayRequestForm) => { const { method: paymentMethod } = usePaymentMethod(); const isCashu = paymentMethod === 'cashu'; const hasZapSplit = zapArrays.length > 0 && !isCashu; - const { nutzapRequest } = useCashu(); + const { nutzapRequest } = useNutzapRequest(); const handleSubmit = async (e?: React.FormEvent) => { e?.preventDefault(); @@ -74,7 +72,7 @@ const PayRequestForm = ({ account, status, onClose }: IPayRequestForm) => { const splitData = { hasZapSplit, zapSplitAccounts, splitValues }; if (isCashu) { - nutzapRequest(api, account, amount, zapComment, status); + nutzapRequest(account, amount, zapComment, status); dispatch(closeModal('PAY_REQUEST')); return; } diff --git a/src/features/zap/hooks/useCashu.ts b/src/features/zap/hooks/useCashu.ts deleted file mode 100644 index dbbe63144..000000000 --- a/src/features/zap/hooks/useCashu.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { produce } from 'immer'; -import { create } from 'zustand'; - -import { MastodonClient } from 'soapbox/api/MastodonClient.ts'; -import { Transactions, WalletData, baseWalletSchema, transactionsSchema } from 'soapbox/schemas/wallet.ts'; -import toast from 'soapbox/toast.tsx'; - -import type { Account as AccountEntity, Status as StatusEntity } from 'soapbox/types/entities.ts'; - -interface IWalletInfo { - mints: string[]; - relays: string[]; -} - -interface ICashuState { - nutzapsList: Record; - isLoading: boolean; - error: string | null; - wallet: WalletData | null; - transactions: Transactions | null; - - createWallet: (api: MastodonClient, walletInfo: IWalletInfo) => Promise; - getTransactions: (api: MastodonClient) => Promise; - getWallet: (api: MastodonClient, hasMessage?: boolean) => Promise; - nutzapRequest: (api: MastodonClient, account: AccountEntity, amount: number, comment: string, status?: StatusEntity) => void; -} - -// TODO: Convert it into a custom hook after the backend is finished -export const useCashu = create((set) => ({ - nutzapsList: {}, - isLoading: false, - error: null, - wallet: null, - transactions: null, - - createWallet: async (api, walletInfo) => { - try { - const response = await api.put('/api/v1/ditto/cashu/wallet', walletInfo); - const data = await response.json(); - if (data) { - const normalizedData = baseWalletSchema.parse(data); - toast.success('Wallet created successfully'); // TO DO: create translated text - set({ wallet: normalizedData }); - } - } catch (e) { - toast.error('An error has occurred'); // TO DO: create translated text - } - }, - - getWallet: async (api, hasMessage = true) => { - try { - const response = await api.get('/api/v1/ditto/cashu/wallet'); - const data: WalletData = await response.json(); - if (data) { - const normalizedData = baseWalletSchema.parse(data); - set({ wallet: normalizedData }); - } - } catch (error) { - if (hasMessage) toast.error('Wallet not found'); - } - }, - - getTransactions: async (api) => { - try { - const response = await api.get('/api/v1/ditto/cashu/transactions'); - const data: WalletData = await response.json(); - if (data) { - const normalizedData = transactionsSchema.parse(data); - set({ transactions: normalizedData }); - } - } catch (error) { - toast.error('Transactions not found'); - } - }, - - nutzapRequest: async (api, account, amount, comment, status) => { - set((state) => ({ ...state, isLoading: true, error: null })); - - try { - const response = await api.post('/api/v1/ditto/cashu/nutzap', { - amount, - comment, - account_id: account.id, - status_id: status?.id, - }); - - const data = await response.json(); - - set(produce((state) => { - if (status) state.nutzapsList[status.id] = { status, amount, comment }; - state.isLoading = false; - })); - - toast.success(data.message || 'Nutzap sent successfully!'); - } catch (e) { - set((state) => ({ ...state, isLoading: false, error: e instanceof Error ? e.message : 'An unexpected error occurred' })); - toast.error(e instanceof Error ? e.message : 'An unexpected error occurred'); - } - }, -})); \ No newline at end of file diff --git a/src/features/zap/hooks/useHooks.ts b/src/features/zap/hooks/useHooks.ts new file mode 100644 index 000000000..320a724d1 --- /dev/null +++ b/src/features/zap/hooks/useHooks.ts @@ -0,0 +1,138 @@ +import { useEffect, useState } from 'react'; + +import { useApi } from 'soapbox/hooks/useApi.ts'; +import { Transactions, WalletData, baseWalletSchema, transactionsSchema } from 'soapbox/schemas/wallet.ts'; +import toast from 'soapbox/toast.tsx'; + +import type { Account as AccountEntity, Status as StatusEntity } from 'soapbox/types/entities.ts'; + +interface IWalletInfo { + mints: string[]; + relays: string[]; +} + +const useWallet = () => { + const api = useApi(); + const [wallet, setWallet] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + const createWallet = async (walletInfo: IWalletInfo) => { + setIsLoading(true); + try { + const response = await api.put('/api/v1/ditto/cashu/wallet', walletInfo); + const data = await response.json(); + if (data) { + const normalizedData = baseWalletSchema.parse(data); + toast.success('Wallet created successfully'); + setWallet(normalizedData); + } + } catch (err) { + const messageError = err instanceof Error ? err.message : 'An error has occurred'; + setError(messageError); + toast.error(messageError); + } finally { + setIsLoading(false); + } + }; + + const getWallet = async (hasMessage = true) => { + setIsLoading(true); + try { + const response = await api.get('/api/v1/ditto/cashu/wallet'); + const data: WalletData = await response.json(); + if (data) { + const normalizedData = baseWalletSchema.parse(data); + setWallet(normalizedData); + } + } catch (err) { + const messageError = err instanceof Error ? err.message : 'Wallet not found'; + if (hasMessage) toast.error(messageError); + setError(messageError); + } finally { + setIsLoading(false); + } + }; + + useEffect(() => { + if (!wallet) { + getWallet(false); + } + }, [wallet]); + + return { wallet, isLoading, error, createWallet, getWallet }; +}; + +const useTransactions = () => { + const api = useApi(); + const [transactions, setTransactions] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + const getTransactions = async () => { + setIsLoading(true); + try { + const response = await api.get('/api/v1/ditto/cashu/transactions'); + const data: Transactions = await response.json(); + if (data) { + const normalizedData = transactionsSchema.parse(data); + setTransactions(normalizedData); + } + } catch (err) { + const messageError = err instanceof Error ? err.message : 'Transactions not found'; + toast.error(messageError); + setError(messageError); + } finally { + setIsLoading(false); + } + }; + + useEffect(() => { + if (!transactions) { + getTransactions(); + } + }, [transactions]); + + return { transactions, isLoading, error, getTransactions }; +}; + +const useNutzapRequest = () => { + const api = useApi(); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + const [nutzapsList, setNutzapsList] = useState>({}); + + const nutzapRequest = async (account: AccountEntity, amount: number, comment: string, status?: StatusEntity) => { + setIsLoading(true); + setError(null); + try { + const response = await api.post('/api/v1/ditto/cashu/nutzap', { + amount, + comment, + account_id: account.id, + status_id: status?.id, + }); + + const data = await response.json(); + + if (status) { + setNutzapsList((prevState) => ({ + ...prevState, + [status.id]: { status, amount, comment }, + })); + } + + toast.success(data.message || 'Nutzap sent successfully!'); + } catch (err) { + const messageError = err instanceof Error ? err.message : 'An unexpected error occurred'; + setError(messageError); + toast.error(messageError); + } finally { + setIsLoading(false); + } + }; + + return { nutzapsList, isLoading, error, nutzapRequest }; +}; + +export { useWallet, useTransactions, useNutzapRequest }; \ No newline at end of file