Update wallet-relays and wallet-mints to use react query

merge-requests/3367/head
danidfra 2025-04-25 11:21:52 -03:00
rodzic 801bf70d6b
commit 941d637c1a
12 zmienionych plików z 86 dodań i 65 usunięć

Wyświetl plik

@ -2,13 +2,14 @@ import { List as ImmutableList } from 'immutable';
import { useEffect, useMemo } from 'react';
import { FormattedMessage } from 'react-intl';
import { fetchZaps, expandZaps } from 'soapbox/actions/interactions.ts';
import ScrollableList from 'soapbox/components/scrollable-list.tsx';
import Modal from 'soapbox/components/ui/modal.tsx';
import Spinner from 'soapbox/components/ui/spinner.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import AccountContainer from 'soapbox/containers/account-container.tsx';
import { useWalletStore, useZappedByCashu } from 'soapbox/features/zap/hooks/useHooks.ts';
import { useWalletStore, useZappedByCashu } from 'soapbox/features/wallet/hooks/useHooks.ts';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { shortNumberFormat } from 'soapbox/utils/numbers.tsx';

Wyświetl plik

@ -7,13 +7,14 @@ import { useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import Button from 'soapbox/components/ui/button.tsx';
import HStack from 'soapbox/components/ui/hstack.tsx';
import Icon from 'soapbox/components/ui/icon.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import Balance from 'soapbox/features/wallet/components/balance.tsx';
import { useWallet } from 'soapbox/features/zap/hooks/useHooks.ts';
import { useWallet } from 'soapbox/features/wallet/hooks/useHooks.ts';
const messages = defineMessages({
wallet: { id: 'wallet.title', defaultMessage: 'Wallet' },

Wyświetl plik

@ -7,6 +7,7 @@ import QRCode from 'qrcode.react';
import { useCallback, useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import CopyableInput from 'soapbox/components/copyable-input.tsx';
import Button from 'soapbox/components/ui/button.tsx';
import Divider from 'soapbox/components/ui/divider.tsx';
@ -17,7 +18,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 { useTransactions, useWallet } from 'soapbox/features/zap/hooks/useHooks.ts';
import { useTransactions, useWallet } from 'soapbox/features/wallet/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';

Wyświetl plik

@ -3,6 +3,7 @@ import plusIcon from '@tabler/icons/outline/square-rounded-plus.svg';
import { useState } from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import Button from 'soapbox/components/ui/button.tsx';
import FormActions from 'soapbox/components/ui/form-actions.tsx';
import Form from 'soapbox/components/ui/form.tsx';
@ -12,7 +13,7 @@ import Stack from 'soapbox/components/ui/stack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import Tooltip from 'soapbox/components/ui/tooltip.tsx';
import { MintEditor } from 'soapbox/features/wallet/components/editable-lists.tsx';
import { useCreateWallet } from 'soapbox/features/zap/hooks/useHooks.ts';
import { useCreateWallet } from 'soapbox/features/wallet/hooks/useHooks.ts';
import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts';
const messages = defineMessages({

Wyświetl plik

@ -9,7 +9,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 { useTransactions } from 'soapbox/features/zap/hooks/useHooks.ts';
import { useTransactions } from 'soapbox/features/wallet/hooks/useHooks.ts';
import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts';
const themes = {

Wyświetl plik

@ -7,30 +7,27 @@ import Spinner from 'soapbox/components/ui/spinner.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import { MintEditor } from 'soapbox/features/wallet/components/editable-lists.tsx';
import { useWallet } from 'soapbox/features/zap/hooks/useHooks.ts';
import { useApi } from 'soapbox/hooks/useApi.ts';
import { useUpdateWallet, useWallet } from 'soapbox/features/wallet/hooks/useHooks.ts';
import toast from 'soapbox/toast.tsx';
import { isURL } from 'soapbox/utils/auth.ts';
const messages = defineMessages({
title: { id: 'wallet.mints', defaultMessage: 'Mints' },
loadingError: { id: 'wallet.loading_error', defaultMessage: 'An unexpected error occurred while loading your wallet data.' },
error: { id: 'wallet.mints.error', defaultMessage: 'Failed to update mints.' },
empty: { id: 'wallet.mints.empty', defaultMessage: 'At least one mint is required.' },
url: { id: 'wallet.invalid_url', defaultMessage: 'All strings must be valid URLs.' },
success: { id: 'wallet.mints.success', defaultMessage: 'Mints updated with success!' },
send: { id: 'common.send', defaultMessage: 'Send' },
});
const WalletMints = () => {
const intl = useIntl();
const api = useApi();
const { walletData } = useWallet();
const [relays, setRelays] = useState<string[]>([]);
const [initialMints, setInitialMints] = useState<string[]>([]);
const [mints, setMints] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(true);
const { updateWallet, isLoading } = useUpdateWallet();
const [isInitialLoading, setIsInitialLoading] = useState<boolean>(isLoading);
const [hasError, setHasError] = useState<boolean>(false);
const handleClick = async () =>{
@ -48,21 +45,16 @@ const WalletMints = () => {
return;
}
try {
await api.put('/api/v1/ditto/cashu/wallet', { mints: mints, relays: relays });
await updateWallet({ mints, relays });
setInitialMints(mints);
toast.success(intl.formatMessage(messages.success));
} catch (error) {
const errorMessage = error instanceof Error ? error.message : intl.formatMessage(messages.error);
toast.error(errorMessage);
console.error(error);
}
};
useEffect(
() => {
setIsLoading(true);
setIsInitialLoading(true);
setHasError(false);
if (walletData) {
@ -75,15 +67,13 @@ const WalletMints = () => {
setHasError(true);
toast.error(intl.formatMessage(messages.loadingError));
} finally {
setIsLoading(false);
setIsInitialLoading(false);
}
} else {
// Handle the case when wallet is null or undefined
setIsLoading(false);
if (walletData === undefined) { // wallet is still loading
// Keep loading state true
setIsLoading(true);
} else if (walletData === null) { // wallet failed to load
setIsInitialLoading(false);
if (walletData === undefined) {
setIsInitialLoading(true);
} else if (walletData === null) {
setHasError(true);
toast.error(intl.formatMessage(messages.loadingError));
}
@ -94,7 +84,7 @@ const WalletMints = () => {
return (
<Column label={intl.formatMessage(messages.title)} >
{(() => {
if (isLoading) {
if (isInitialLoading) {
return (
<Stack space={2} className='flex h-32 items-center justify-center'>
<Spinner />
@ -110,7 +100,7 @@ const WalletMints = () => {
return (
<Stack space={2}>
<MintEditor items={mints} setItems={setMints} />
<Button className='w-full' theme='primary' onClick={handleClick}>
<Button className='w-full' theme='primary' onClick={handleClick} disabled={isLoading}>
{intl.formatMessage(messages.send)}
</Button>
</Stack>

Wyświetl plik

@ -7,30 +7,27 @@ import Spinner from 'soapbox/components/ui/spinner.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import { RelayEditor } from 'soapbox/features/wallet/components/editable-lists.tsx';
import { useWallet } from 'soapbox/features/zap/hooks/useHooks.ts';
import { useApi } from 'soapbox/hooks/useApi.ts';
import { useUpdateWallet, useWallet } from 'soapbox/features/wallet/hooks/useHooks.ts';
import toast from 'soapbox/toast.tsx';
import { isURL } from 'soapbox/utils/auth.ts';
const messages = defineMessages({
title: { id: 'wallet.relays', defaultMessage: 'Wallet Relays' },
loadingError: { id: 'wallet.loading_error', defaultMessage: 'An unexpected error occurred while loading your wallet data.' },
error: { id: 'wallet.relays.error', defaultMessage: 'Failed to update relays.' },
empty: { id: 'wallet.relays.empty', defaultMessage: 'At least one relay is required.' },
url: { id: 'wallet.invalid_url', defaultMessage: 'All strings must be valid URLs.' },
success: { id: 'wallet.relays.success', defaultMessage: 'Relays updated with success!' },
send: { id: 'common.send', defaultMessage: 'Send' },
});
const WalletRelays = () => {
const intl = useIntl();
const api = useApi();
const { walletData } = useWallet();
const [relays, setRelays] = useState<string[]>([]);
const [initialRelays, setInitialRelays] = useState<string[]>([]);
const [mints, setMints] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(true);
const { updateWallet, isLoading } = useUpdateWallet();
const [isInitialLoading, setIsInitialLoading] = useState<boolean>(isLoading);
const [hasError, setHasError] = useState<boolean>(false);
const handleClick = async () =>{
@ -49,19 +46,16 @@ const WalletRelays = () => {
}
try {
await api.put('/api/v1/ditto/cashu/wallet', { mints: mints, relays: relays });
await updateWallet({ mints, relays });
setInitialRelays(relays);
toast.success(intl.formatMessage(messages.success));
} catch (error) {
const errorMessage = error instanceof Error ? error.message : intl.formatMessage(messages.error);
toast.error(errorMessage);
console.error(error);
}
};
useEffect(
() => {
setIsLoading(true);
setIsInitialLoading(true);
setHasError(false);
if (walletData) {
@ -74,12 +68,12 @@ const WalletRelays = () => {
setHasError(true);
toast.error(intl.formatMessage(messages.loadingError));
} finally {
setIsLoading(false);
setIsInitialLoading(false);
}
} else {
setIsLoading(false);
setIsInitialLoading(false);
if (walletData === undefined) {
setIsLoading(true);
setIsInitialLoading(true);
} else if (walletData === null) {
setHasError(true);
toast.error(intl.formatMessage(messages.loadingError));
@ -91,7 +85,7 @@ const WalletRelays = () => {
return (
<Column label={intl.formatMessage(messages.title)} >
{(() => {
if (isLoading) {
if (isInitialLoading) {
return (
<Stack space={2} className='flex h-32 items-center justify-center'>
<Spinner />
@ -107,7 +101,7 @@ const WalletRelays = () => {
return (
<Stack space={2}>
<RelayEditor items={relays} setItems={setRelays} />
<Button className='w-full' theme='primary' onClick={handleClick}>
<Button className='w-full' theme='primary' onClick={handleClick} disabled={isLoading}>
{intl.formatMessage(messages.send)}
</Button>
</Stack>

Wyświetl plik

@ -1,10 +1,11 @@
import { useEffect, useRef, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { Column } from 'soapbox/components/ui/column.tsx';
import Spinner from 'soapbox/components/ui/spinner.tsx';
import Transactions from 'soapbox/features/wallet/components/transactions.tsx';
import { useTransactions } from 'soapbox/features/zap/hooks/useHooks.ts';
import { useTransactions } from 'soapbox/features/wallet/hooks/useHooks.ts';
const messages = defineMessages({
title: { id: 'wallet.transactions', defaultMessage: 'Transactions' },

Wyświetl plik

@ -1,4 +1,5 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { defineMessages, useIntl } from 'react-intl';
import { create } from 'zustand';
import { useApi } from 'soapbox/hooks/useApi.ts';
@ -7,9 +8,13 @@ import toast from 'soapbox/toast.tsx';
import type { Account as AccountEntity, Status as StatusEntity } from 'soapbox/types/entities.ts';
const messages = defineMessages({
success: { id: 'wallet.success', defaultMessage: 'Data updated with success!' },
error: { id: 'wallet.error', defaultMessage: 'Failed to update.' },
});
interface WalletState {
wallet: WalletData | null;
// acceptsZapsCashu: boolean;
transactions: Transactions | null;
zapCashuList: string[];
nutzappedRecord: NutzappedRecord;
@ -37,7 +42,7 @@ interface IZapCashuPayload {
account: AccountEntity;
amount: number;
comment: string;
status : StatusEntity;
status?: StatusEntity;
}
const useWalletStore = create<WalletState>((set) => ({
@ -148,7 +153,6 @@ const useTransactions = () => {
mutationFn: async () => {
if (!nextTransaction || !transactions) {
return false;
// throw new Error('No more transactions');
}
const response = await api.get(nextTransaction);
@ -256,4 +260,33 @@ const useZappedByCashu = (statusId: string) => {
};
};
export { useWalletStore, useWallet, useCreateWallet, useTransactions, useZapCashuRequest, useZappedByCashu };
const useUpdateWallet = () => {
const api = useApi();
const queryClient = useQueryClient();
const intl = useIntl();
const updateWallet = useMutation({
mutationFn: async ({ mints, relays }: IWalletInfo) => {
await api.put('/api/v1/ditto/cashu/wallet', { mints: mints, relays: relays });
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['wallet'] });
toast.success(intl.formatMessage(messages.success));
},
onError: (err: Error) => {
const errorMessage = err instanceof Error ? err.message : intl.formatMessage(messages.error);
toast.error(errorMessage);
console.error(err);
},
});
return {
updateWallet: updateWallet.mutateAsync,
isSuccess: updateWallet.isSuccess,
error: updateWallet.error,
isLoading: updateWallet.isPending,
};
};
export { useWalletStore, useWallet, useCreateWallet, useTransactions, useZapCashuRequest, useZappedByCashu, useUpdateWallet };

Wyświetl plik

@ -2,6 +2,7 @@ import moreIcon from '@tabler/icons/outline/dots-circle-horizontal.svg';
import { useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import List, { ListItem } from 'soapbox/components/list.tsx';
import Button from 'soapbox/components/ui/button.tsx';
import { Card, CardBody, CardHeader, CardTitle } from 'soapbox/components/ui/card.tsx';
@ -13,7 +14,7 @@ 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 { useTransactions, useWallet } from 'soapbox/features/zap/hooks/useHooks.ts';
import { useTransactions, useWallet } from 'soapbox/features/wallet/hooks/useHooks.ts';
import { usePaymentMethod } from 'soapbox/features/zap/usePaymentMethod.ts';
import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts';

Wyświetl plik

@ -22,7 +22,7 @@ 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 { useZapCashuRequest } from 'soapbox/features/zap/hooks/useHooks.ts';
import { useZapCashuRequest } from 'soapbox/features/wallet/hooks/useHooks.ts';
import { usePaymentMethod } from 'soapbox/features/zap/usePaymentMethod.ts';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { emojifyText } from 'soapbox/utils/emojify.tsx';
@ -46,23 +46,23 @@ interface IPayRequestForm {
}
const messages = defineMessages({
zap_button_rounded: { id: 'zap.button.text.rounded', defaultMessage: 'Zap {amount}K sats' },
zap_button: { id: 'payment_method.button.text.raw', defaultMessage: 'Zap {amount} sats' },
zap_commentPlaceholder: { id: 'payment_method.comment_input.placeholder', defaultMessage: 'Optional comment' },
button_rounded: { id: 'zap.button.text.rounded', defaultMessage: 'Zap {amount}K sats' },
button: { id: 'payment_method.button.text.raw', defaultMessage: 'Zap {amount} sats' },
commentPlaceholder: { id: 'payment_method.comment_input.placeholder', defaultMessage: 'Optional comment' },
});
const PayRequestForm = ({ account, status, onClose }: IPayRequestForm) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const [zapComment, setZapComment] = useState('');
const [comment, setComment] = useState('');
const [amount, setAmount] = useState(50);
const { zapArrays, zapSplitData, receiveAmount } = useZapSplit(status, account);
const splitValues = zapSplitData.splitValues;
const { method: paymentMethod, changeMethod } = usePaymentMethod();
const isCashu = paymentMethod === 'cashu';
const hasZapSplit = zapArrays.length > 0 && !isCashu;
const { zapCashuRequest } = useZapCashuRequest();
const { zapCashu } = useZapCashuRequest();
const handleSubmit = async (e?: React.FormEvent<Element>) => {
e?.preventDefault();
@ -70,14 +70,14 @@ const PayRequestForm = ({ account, status, onClose }: IPayRequestForm) => {
const splitData = { hasZapSplit, zapSplitAccounts, splitValues };
if (isCashu) {
await zapCashuRequest(account, amount, zapComment, status);
await zapCashu({ account, amount, comment, status });
dispatch(closeModal('PAY_REQUEST'));
return;
}
const invoice = hasZapSplit
? await dispatch(zap(account, status, zapSplitData.receiveAmount * 1000, zapComment))
: await dispatch(zap(account, status, amount * 1000, zapComment));
? await dispatch(zap(account, status, zapSplitData.receiveAmount * 1000, comment))
: await dispatch(zap(account, status, amount * 1000, comment));
if (!invoice) {
dispatch(closeModal('PAY_REQUEST'));
@ -106,9 +106,9 @@ const PayRequestForm = ({ account, status, onClose }: IPayRequestForm) => {
const renderPaymentButtonText = () => {
if (amount >= 1000) {
return intl.formatMessage(messages.zap_button_rounded, { amount: Math.round((amount / 1000) * 10) / 10 });
return intl.formatMessage(messages.button_rounded, { amount: Math.round((amount / 1000) * 10) / 10 });
}
return intl.formatMessage(messages.zap_button, { amount: amount });
return intl.formatMessage(messages.button, { amount: amount });
};
useEffect(() => {
@ -212,7 +212,7 @@ const PayRequestForm = ({ account, status, onClose }: IPayRequestForm) => {
</Stack>
<div className='w-full'>
<Input onChange={e => setZapComment(e.target.value)} type='text' placeholder={intl.formatMessage(messages.zap_commentPlaceholder)} />
<Input onChange={e => setComment(e.target.value)} type='text' placeholder={intl.formatMessage(messages.commentPlaceholder)} />
</div>
{hasZapSplit ? <Stack space={2}>

Wyświetl plik

@ -1731,6 +1731,7 @@
"wallet.button.withdraw": "Withdraw",
"wallet.create_wallet.question": "Do you want create one?",
"wallet.create_wallet.title": "You don't have a wallet",
"wallet.error": "Failed to update.",
"wallet.hidden.balance": "••••••",
"wallet.invalid_url": "All strings must be valid URLs.",
"wallet.loading": "Loading…",
@ -1738,16 +1739,13 @@
"wallet.management": "Wallet Management",
"wallet.mints": "Mints",
"wallet.mints.empty": "At least one mint is required.",
"wallet.mints.error": "Failed to update mints.",
"wallet.mints.success": "Mints updated with success!",
"wallet.payment": "Payment Method",
"wallet.relays": "Wallet Relays",
"wallet.relays.empty": "At least one relay is required.",
"wallet.relays.error": "Failed to update relays.",
"wallet.relays.success": "Relays updated with success!",
"wallet.retry": "Retry Now",
"wallet.retrying": "Retrying in {seconds}s…",
"wallet.sats": "{amount} sats",
"wallet.success": "Data updated with success!",
"wallet.title": "Wallet",
"wallet.transactions": "Transactions",
"wallet.transactions.more": "More",