kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Refactor wallet logic to handle Zap Cashu data
rodzic
fc7a8c849c
commit
92aa009de9
|
@ -48,7 +48,7 @@ import DropdownMenu from 'soapbox/components/dropdown-menu/index.ts';
|
||||||
import PureStatusReactionWrapper from 'soapbox/components/pure-status-reaction-wrapper.tsx';
|
import PureStatusReactionWrapper from 'soapbox/components/pure-status-reaction-wrapper.tsx';
|
||||||
import StatusActionButton from 'soapbox/components/status-action-button.tsx';
|
import StatusActionButton from 'soapbox/components/status-action-button.tsx';
|
||||||
import HStack from 'soapbox/components/ui/hstack.tsx';
|
import HStack from 'soapbox/components/ui/hstack.tsx';
|
||||||
import { useNutzapRequest, useWallet } from 'soapbox/features/zap/hooks/useHooks.ts';
|
import { useZapCashuRequest, useWallet } from 'soapbox/features/zap/hooks/useHooks.ts';
|
||||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||||
import { useDislike } from 'soapbox/hooks/useDislike.ts';
|
import { useDislike } from 'soapbox/hooks/useDislike.ts';
|
||||||
|
@ -181,8 +181,8 @@ const PureStatusActionBar: React.FC<IPureStatusActionBar> = ({
|
||||||
const { boostModal, deleteModal } = useSettings();
|
const { boostModal, deleteModal } = useSettings();
|
||||||
|
|
||||||
const { wallet } = useWallet();
|
const { wallet } = useWallet();
|
||||||
const { nutzapsList } = useNutzapRequest();
|
const { zapCashuList } = useZapCashuRequest();
|
||||||
const isNutzapped = Object.keys(nutzapsList).some((nutzap)=> nutzap === status.id); // TODO: Remove "getWallet" after been in backend
|
const isZappedCashu = zapCashuList.some((zapCashu)=> zapCashu === status.id);
|
||||||
|
|
||||||
const { account } = useOwnAccount();
|
const { account } = useOwnAccount();
|
||||||
const isStaff = account ? account.staff : false;
|
const isStaff = account ? account.staff : false;
|
||||||
|
@ -855,9 +855,9 @@ const PureStatusActionBar: React.FC<IPureStatusActionBar> = ({
|
||||||
color='accent'
|
color='accent'
|
||||||
filled
|
filled
|
||||||
onClick={handleZapClick}
|
onClick={handleZapClick}
|
||||||
active={status.nutzapped || status.zapped || isNutzapped}
|
active={status.zapped_cashu || status.zapped || isZappedCashu}
|
||||||
theme={statusActionButtonTheme}
|
theme={statusActionButtonTheme}
|
||||||
count={status?.zaps_amount ? status.zaps_amount / 1000 : 0}
|
count={(status?.zaps_amount ?? 0) / 1000 + (status?.zaps_amount_cashu ?? 0)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ import DropdownMenu from 'soapbox/components/dropdown-menu/index.ts';
|
||||||
import StatusActionButton from 'soapbox/components/status-action-button.tsx';
|
import StatusActionButton from 'soapbox/components/status-action-button.tsx';
|
||||||
import StatusReactionWrapper from 'soapbox/components/status-reaction-wrapper.tsx';
|
import StatusReactionWrapper from 'soapbox/components/status-reaction-wrapper.tsx';
|
||||||
import HStack from 'soapbox/components/ui/hstack.tsx';
|
import HStack from 'soapbox/components/ui/hstack.tsx';
|
||||||
import { useNutzapRequest, useWallet } from 'soapbox/features/zap/hooks/useHooks.ts';
|
import { useZapCashuRequest, useWallet } from 'soapbox/features/zap/hooks/useHooks.ts';
|
||||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||||
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
|
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
|
||||||
|
@ -176,8 +176,8 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
const { boostModal, deleteModal } = useSettings();
|
const { boostModal, deleteModal } = useSettings();
|
||||||
|
|
||||||
const { wallet } = useWallet();
|
const { wallet } = useWallet();
|
||||||
const { nutzapsList } = useNutzapRequest();
|
const { zapCashuList } = useZapCashuRequest();
|
||||||
const isNutzapped = Object.keys(nutzapsList).some((nutzap)=> nutzap === status.id);
|
const isZappedCashu = zapCashuList.some((zapCashu)=> zapCashu === status.id);
|
||||||
|
|
||||||
const { account } = useOwnAccount();
|
const { account } = useOwnAccount();
|
||||||
const isStaff = account ? account.staff : false;
|
const isStaff = account ? account.staff : false;
|
||||||
|
@ -845,9 +845,9 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
color='accent'
|
color='accent'
|
||||||
filled
|
filled
|
||||||
onClick={handleZapClick}
|
onClick={handleZapClick}
|
||||||
active={status.nutzapped || status.zapped || isNutzapped}
|
active={status.zapped_cashu || status.zapped || isZappedCashu}
|
||||||
theme={statusActionButtonTheme}
|
theme={statusActionButtonTheme}
|
||||||
count={status?.zaps_amount ? status.zaps_amount / 1000 : 0}
|
count={(status?.zaps_amount ?? 0) / 1000 + (status?.zaps_amount_cashu ?? 0)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -206,13 +206,13 @@ const StatusInteractionBar: React.FC<IStatusInteractionBar> = ({ status }): JSX.
|
||||||
};
|
};
|
||||||
|
|
||||||
const getZaps = () => {
|
const getZaps = () => {
|
||||||
if (status.zaps_amount) {
|
if (status.zaps_amount || status.zaps_amount_cashu) {
|
||||||
return (
|
return (
|
||||||
<InteractionCounter count={status.zaps_amount / 1000} onClick={handleOpenZapsModal}>
|
<InteractionCounter count={(status.zaps_amount ?? 0) / 1000 + (status.zaps_amount_cashu ?? 0)} onClick={handleOpenZapsModal}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='status.interactions.zaps'
|
id='status.interactions.zaps'
|
||||||
defaultMessage='{count, plural, one {Zap} other {Zaps}}'
|
defaultMessage='{count, plural, one {Zap} other {Zaps}}'
|
||||||
values={{ count: status.zaps_amount }}
|
values={{ count: (status.zaps_amount ?? 0) + (status.zaps_amount_cashu ?? 0) }}
|
||||||
/>
|
/>
|
||||||
</InteractionCounter>
|
</InteractionCounter>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,7 +21,7 @@ import Input from 'soapbox/components/ui/input.tsx';
|
||||||
import Stack from 'soapbox/components/ui/stack.tsx';
|
import Stack from 'soapbox/components/ui/stack.tsx';
|
||||||
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
||||||
import Text from 'soapbox/components/ui/text.tsx';
|
import Text from 'soapbox/components/ui/text.tsx';
|
||||||
import { useNutzapRequest } from 'soapbox/features/zap/hooks/useHooks.ts';
|
import { useZapCashuRequest } from 'soapbox/features/zap/hooks/useHooks.ts';
|
||||||
import { usePaymentMethod } from 'soapbox/features/zap/usePaymentMethod.ts';
|
import { usePaymentMethod } from 'soapbox/features/zap/usePaymentMethod.ts';
|
||||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||||
import { emojifyText } from 'soapbox/utils/emojify.tsx';
|
import { emojifyText } from 'soapbox/utils/emojify.tsx';
|
||||||
|
@ -63,7 +63,7 @@ const PayRequestForm = ({ account, status, onClose }: IPayRequestForm) => {
|
||||||
const { method: paymentMethod } = usePaymentMethod();
|
const { method: paymentMethod } = usePaymentMethod();
|
||||||
const isCashu = paymentMethod === 'cashu';
|
const isCashu = paymentMethod === 'cashu';
|
||||||
const hasZapSplit = zapArrays.length > 0 && !isCashu;
|
const hasZapSplit = zapArrays.length > 0 && !isCashu;
|
||||||
const { nutzapRequest } = useNutzapRequest();
|
const { zapCashuRequest } = useZapCashuRequest();
|
||||||
|
|
||||||
const handleSubmit = async (e?: React.FormEvent<Element>) => {
|
const handleSubmit = async (e?: React.FormEvent<Element>) => {
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
|
@ -71,7 +71,7 @@ const PayRequestForm = ({ account, status, onClose }: IPayRequestForm) => {
|
||||||
const splitData = { hasZapSplit, zapSplitAccounts, splitValues };
|
const splitData = { hasZapSplit, zapSplitAccounts, splitValues };
|
||||||
|
|
||||||
if (isCashu) {
|
if (isCashu) {
|
||||||
await nutzapRequest(account, amount, zapComment, status);
|
await zapCashuRequest(account, amount, zapComment, status);
|
||||||
dispatch(closeModal('PAY_REQUEST'));
|
dispatch(closeModal('PAY_REQUEST'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,13 @@ import type { Account as AccountEntity, Status as StatusEntity } from 'soapbox/t
|
||||||
interface WalletState {
|
interface WalletState {
|
||||||
wallet: WalletData | null;
|
wallet: WalletData | null;
|
||||||
transactions: Transactions | null;
|
transactions: Transactions | null;
|
||||||
nutzapsList: Record<string, { status: StatusEntity; amount: number; comment: string }>; // TODO: remove
|
zapCashuList: string[];
|
||||||
prevTransaction?: string | null;
|
prevTransaction?: string | null;
|
||||||
nextTransaction?: string | null;
|
nextTransaction?: string | null;
|
||||||
|
|
||||||
setWallet: (wallet: WalletData | null) => void;
|
setWallet: (wallet: WalletData | null) => void;
|
||||||
setTransactions: (transactions: Transactions | null, prevTransaction?: string | null, nextTransaction?: string | null) => void;
|
setTransactions: (transactions: Transactions | null, prevTransaction?: string | null, nextTransaction?: string | null) => void;
|
||||||
addNutzap: (statusId: string, data: { status: StatusEntity; amount: number; comment: string }) => void;
|
addZapCashu: (statusId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IWalletInfo {
|
interface IWalletInfo {
|
||||||
|
@ -29,16 +29,16 @@ const useWalletStore = create<WalletState>((set) => ({
|
||||||
transactions: null,
|
transactions: null,
|
||||||
prevTransaction: null,
|
prevTransaction: null,
|
||||||
nextTransaction: null,
|
nextTransaction: null,
|
||||||
nutzapsList: {},
|
zapCashuList: [],
|
||||||
|
|
||||||
setWallet: (wallet) => set({ wallet }),
|
setWallet: (wallet) => set({ wallet }),
|
||||||
setTransactions: (transactions, prevTransaction, nextTransaction) => set({ transactions, prevTransaction, nextTransaction }),
|
setTransactions: (transactions, prevTransaction, nextTransaction) => set({ transactions, prevTransaction, nextTransaction }),
|
||||||
addNutzap: (statusId, data) =>
|
addZapCashu: (statusId) =>
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
nutzapsList: {
|
zapCashuList: [
|
||||||
...state.nutzapsList,
|
...state.zapCashuList,
|
||||||
[statusId]: data,
|
statusId,
|
||||||
},
|
],
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -152,15 +152,15 @@ const useTransactions = () => {
|
||||||
return { transactions, isLoading, error, getTransactions, expandTransactions };
|
return { transactions, isLoading, error, getTransactions, expandTransactions };
|
||||||
};
|
};
|
||||||
|
|
||||||
const useNutzapRequest = () => {
|
const useZapCashuRequest = () => {
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
const { nutzapsList, addNutzap } = useWalletStore();
|
const { zapCashuList, addZapCashu } = useWalletStore();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const { getWallet } = useWallet();
|
const { getWallet } = useWallet();
|
||||||
const { getTransactions } = useTransactions();
|
const { getTransactions } = useTransactions();
|
||||||
|
|
||||||
const nutzapRequest = async (account: AccountEntity, amount: number, comment: string, status?: StatusEntity) => {
|
const zapCashuRequest = async (account: AccountEntity, amount: number, comment: string, status?: StatusEntity) => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
|
@ -174,7 +174,7 @@ const useNutzapRequest = () => {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
addNutzap(status.id, { status, amount, comment });
|
addZapCashu(status.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.success(data.message || 'Zap sent successfully!');
|
toast.success(data.message || 'Zap sent successfully!');
|
||||||
|
@ -189,7 +189,7 @@ const useNutzapRequest = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { nutzapsList, isLoading, error, nutzapRequest };
|
return { zapCashuList, isLoading, error, zapCashuRequest };
|
||||||
};
|
};
|
||||||
|
|
||||||
export { useWallet, useTransactions, useNutzapRequest };
|
export { useWallet, useTransactions, useZapCashuRequest };
|
|
@ -77,7 +77,7 @@ export const StatusRecord = ImmutableRecord({
|
||||||
reblogs_count: 0,
|
reblogs_count: 0,
|
||||||
replies_count: 0,
|
replies_count: 0,
|
||||||
zaps_amount: 0,
|
zaps_amount: 0,
|
||||||
nutzaps_amount: 0,
|
zaps_amount_cashu: 0,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
spoiler_text: '',
|
spoiler_text: '',
|
||||||
tags: ImmutableList<ImmutableMap<string, any>>(),
|
tags: ImmutableList<ImmutableMap<string, any>>(),
|
||||||
|
@ -86,7 +86,7 @@ export const StatusRecord = ImmutableRecord({
|
||||||
url: '',
|
url: '',
|
||||||
visibility: 'public' as StatusVisibility,
|
visibility: 'public' as StatusVisibility,
|
||||||
zapped: false,
|
zapped: false,
|
||||||
nutzapped: false,
|
zapped_cashu: false,
|
||||||
event: null as ReturnType<typeof EventRecord> | null,
|
event: null as ReturnType<typeof EventRecord> | null,
|
||||||
|
|
||||||
// Internal fields
|
// Internal fields
|
||||||
|
|
|
@ -222,20 +222,13 @@ const simulateDislike = (
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Simulate zap of status for optimistic interactions */
|
/** Simulate zap of status for optimistic interactions */
|
||||||
const simulatePayment = (state: State, statusId: string, paid: boolean, method: 'cashu' | 'zap'): State => {
|
const simulatePayment = (state: State, statusId: string, zapped: boolean): State => {
|
||||||
const status = state.get(statusId);
|
const status = state.get(statusId);
|
||||||
if (!status) return state;
|
if (!status) return state;
|
||||||
let updatedStatus;
|
|
||||||
|
|
||||||
if (method === 'zap') {
|
const updatedStatus = status.merge({
|
||||||
updatedStatus = status.merge({
|
zapped,
|
||||||
zapped: paid,
|
});
|
||||||
});
|
|
||||||
} else {
|
|
||||||
updatedStatus = status.merge({
|
|
||||||
nutzapped: paid,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.set(statusId, updatedStatus);
|
return state.set(statusId, updatedStatus);
|
||||||
};
|
};
|
||||||
|
@ -295,9 +288,9 @@ export default function statuses(state = initialState, action: AnyAction): State
|
||||||
case DISLIKE_FAIL:
|
case DISLIKE_FAIL:
|
||||||
return state.get(action.status.id) === undefined ? state : state.setIn([action.status.id, 'disliked'], false);
|
return state.get(action.status.id) === undefined ? state : state.setIn([action.status.id, 'disliked'], false);
|
||||||
case ZAP_REQUEST:
|
case ZAP_REQUEST:
|
||||||
return simulatePayment(state, action.status.id, true, 'zap');
|
return simulatePayment(state, action.status.id, true);
|
||||||
case ZAP_FAIL:
|
case ZAP_FAIL:
|
||||||
return simulatePayment(state, action.status.id, false, 'zap');
|
return simulatePayment(state, action.status.id, false);
|
||||||
case REBLOG_REQUEST:
|
case REBLOG_REQUEST:
|
||||||
return state.setIn([action.status.id, 'reblogged'], true);
|
return state.setIn([action.status.id, 'reblogged'], true);
|
||||||
case REBLOG_FAIL:
|
case REBLOG_FAIL:
|
||||||
|
|
|
@ -73,8 +73,8 @@ const baseStatusSchema = z.object({
|
||||||
visibility: z.string().catch('public'),
|
visibility: z.string().catch('public'),
|
||||||
zapped: z.coerce.boolean(),
|
zapped: z.coerce.boolean(),
|
||||||
zaps_amount: z.number().catch(0),
|
zaps_amount: z.number().catch(0),
|
||||||
nutzapped: z.coerce.boolean(),
|
zapped_cashu: z.coerce.boolean(),
|
||||||
nutzaps_amount: z.number().catch(0),
|
zaps_amount_cashu: z.number().catch(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
type BaseStatus = z.infer<typeof baseStatusSchema>;
|
type BaseStatus = z.infer<typeof baseStatusSchema>;
|
||||||
|
|
Ładowanie…
Reference in New Issue