kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Implement "nutzapped_by"
rodzic
f3873b55ba
commit
1eec214a51
|
@ -8,6 +8,7 @@ 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 { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||
import { shortNumberFormat } from 'soapbox/utils/numbers.tsx';
|
||||
|
@ -16,6 +17,7 @@ interface IAccountWithZaps {
|
|||
id: string;
|
||||
comment: string;
|
||||
amount: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface IZapsModal {
|
||||
|
@ -25,16 +27,22 @@ interface IZapsModal {
|
|||
|
||||
const ZapsModal: React.FC<IZapsModal> = ({ onClose, statusId }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const zaps = useAppSelector((state) => state.user_lists.zapped_by.get(statusId)?.items);
|
||||
const next = useAppSelector((state) => state.user_lists.zapped_by.get(statusId)?.next);
|
||||
const nutzaps = useWalletStore().nutzappedRecord[statusId];
|
||||
const { getNutzappedBy } = useZappedByCashu();
|
||||
|
||||
const accounts = useMemo((): ImmutableList<IAccountWithZaps> | undefined => {
|
||||
if (!zaps) return;
|
||||
if (!zaps && !nutzaps) return;
|
||||
|
||||
return zaps
|
||||
.map(({ account, amount, comment }) => ({ id: account, amount, comment }))
|
||||
.flatten() as ImmutableList<IAccountWithZaps>;
|
||||
}, [zaps]);
|
||||
const zappedAccounts = zaps?.map(({ account, amount, comment }) => ({ id: account, amount, comment, type: 'zap' })) || [];
|
||||
const nutzappedAccounts = nutzaps?.map(({ account, amount, comment }) => ({ id: account.id, amount, comment, type: 'nutzap' })) || [];
|
||||
|
||||
const combinedAccounts = [...zappedAccounts, ...nutzappedAccounts];
|
||||
|
||||
return ImmutableList(combinedAccounts);
|
||||
}, [zaps, nutzaps]);
|
||||
|
||||
const fetchData = () => {
|
||||
dispatch(fetchZaps(statusId));
|
||||
|
@ -42,6 +50,7 @@ const ZapsModal: React.FC<IZapsModal> = ({ onClose, statusId }) => {
|
|||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
getNutzappedBy(statusId);
|
||||
}, []);
|
||||
|
||||
const onClickClose = () => {
|
||||
|
@ -56,7 +65,7 @@ const ZapsModal: React.FC<IZapsModal> = ({ onClose, statusId }) => {
|
|||
|
||||
let body;
|
||||
|
||||
if (!zaps || !accounts) {
|
||||
if (!zaps || !nutzaps || !accounts) {
|
||||
body = <Spinner />;
|
||||
} else {
|
||||
const emptyMessage = <FormattedMessage id='status.zaps.empty' defaultMessage='No one has zapped this post yet. When someone does, they will show up here.' />;
|
||||
|
@ -76,7 +85,7 @@ const ZapsModal: React.FC<IZapsModal> = ({ onClose, statusId }) => {
|
|||
return (
|
||||
<div key={index}>
|
||||
<Text weight='bold'>
|
||||
{shortNumberFormat(account.amount / 1000)}
|
||||
{account.type === 'zap' ? shortNumberFormat(account.amount / 1000) : shortNumberFormat(account.amount)}
|
||||
</Text>
|
||||
<AccountContainer id={account.id} note={account.comment} emoji='⚡' />
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
|
|||
import { create } from 'zustand';
|
||||
|
||||
import { useApi } from 'soapbox/hooks/useApi.ts';
|
||||
import { Transactions, WalletData, baseWalletSchema, transactionsSchema } from 'soapbox/schemas/wallet.ts';
|
||||
import { NutzappedEntry, NutzappedRecord, Transactions, WalletData, baseWalletSchema, nutzappedEntry, 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';
|
||||
|
@ -12,11 +12,13 @@ interface WalletState {
|
|||
acceptsZapsCashu: boolean;
|
||||
transactions: Transactions | null;
|
||||
zapCashuList: string[];
|
||||
nutzappedRecord: NutzappedRecord;
|
||||
prevTransaction?: string | null;
|
||||
nextTransaction?: string | null;
|
||||
hasFetchedWallet: boolean;
|
||||
hasFetchedTransactions: boolean;
|
||||
|
||||
setNutzappedRecord: (statusId: string, nutzappedEntry: NutzappedEntry) => void;
|
||||
setAcceptsZapsCashu: (acceptsZapsCashu: boolean) => void;
|
||||
setWallet: (wallet: WalletData | null) => void;
|
||||
setHasFetchedWallet: (hasFetchedWallet: boolean) => void;
|
||||
|
@ -37,9 +39,16 @@ const useWalletStore = create<WalletState>((set) => ({
|
|||
prevTransaction: null,
|
||||
nextTransaction: null,
|
||||
zapCashuList: [],
|
||||
nutzappedRecord: {},
|
||||
hasFetchedWallet: false,
|
||||
hasFetchedTransactions: false,
|
||||
|
||||
setNutzappedRecord: (statusId, nutzappedEntry) => set((state)=> ({
|
||||
nutzappedRecord: {
|
||||
...state.nutzappedRecord,
|
||||
[statusId]: nutzappedEntry,
|
||||
},
|
||||
})),
|
||||
setAcceptsZapsCashu: (acceptsZapsCashu) => set({ acceptsZapsCashu }),
|
||||
setWallet: (wallet) => set({ wallet }),
|
||||
setHasFetchedWallet: (hasFetchedWallet) => set({ hasFetchedWallet }),
|
||||
|
@ -209,4 +218,32 @@ const useZapCashuRequest = () => {
|
|||
return { zapCashuList, isLoading, error, zapCashuRequest };
|
||||
};
|
||||
|
||||
export { useWalletStore, useWallet, useTransactions, useZapCashuRequest };
|
||||
const useZappedByCashu = () => {
|
||||
const api = useApi();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { setNutzappedRecord } = useWalletStore();
|
||||
|
||||
const getNutzappedBy = async (statusId: string) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await api.get(`/api/v1/ditto/cashu/statuses/${statusId}/nutzapped_by`);
|
||||
// const { prev, next } = response.pagination(); // TODO: pagination after Patrick finish
|
||||
const data = await response.json();
|
||||
if (data) {
|
||||
const normalizedData = nutzappedEntry.parse(data);
|
||||
setNutzappedRecord(statusId, normalizedData);
|
||||
}
|
||||
} catch (err) {
|
||||
const messageError = err instanceof Error ? err.message : 'Zaps not found';
|
||||
toast.error('Zaps not foud');
|
||||
setError(messageError);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return { error, isLoading, getNutzappedBy };
|
||||
};
|
||||
|
||||
export { useWalletStore, useWallet, useTransactions, useZapCashuRequest, useZappedByCashu };
|
|
@ -1,6 +1,8 @@
|
|||
import { NSchema as n } from '@nostrify/nostrify';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { accountSchema } from 'soapbox/schemas/account.ts';
|
||||
|
||||
const baseWalletSchema = z.object({
|
||||
pubkey_p2pk: n.id(),
|
||||
mints: z.array(z.string().url()).nonempty(),
|
||||
|
@ -22,12 +24,30 @@ const transactionSchema = z.object({
|
|||
direction: z.enum(['in', 'out']),
|
||||
});
|
||||
|
||||
|
||||
const nutzappedEntry = z.array(
|
||||
z.object({
|
||||
comment: z.string(),
|
||||
amount: z.number(),
|
||||
account: accountSchema,
|
||||
}),
|
||||
);
|
||||
|
||||
const nutzappedRecord = z.record(
|
||||
z.string(),
|
||||
nutzappedEntry,
|
||||
);
|
||||
|
||||
const transactionsSchema = z.array(transactionSchema);
|
||||
|
||||
type NutzappedEntry = z.infer<typeof nutzappedEntry>
|
||||
|
||||
type NutzappedRecord = z.infer<typeof nutzappedRecord>
|
||||
|
||||
type Transactions = z.infer<typeof transactionsSchema>
|
||||
|
||||
type Quote = z.infer<typeof quoteSchema>
|
||||
|
||||
type WalletData = z.infer<typeof baseWalletSchema>;
|
||||
|
||||
export { baseWalletSchema, quoteSchema, transactionsSchema, type WalletData, type Quote, type Transactions };
|
||||
export { baseWalletSchema, quoteSchema, transactionsSchema, nutzappedRecord, nutzappedEntry, type WalletData, type Quote, type Transactions, type NutzappedRecord, type NutzappedEntry };
|
Ładowanie…
Reference in New Issue