Donations: show modal when we can't apply the user's badge

This commit is contained in:
yash-signal
2025-08-29 14:29:53 -05:00
committed by GitHub
parent f0488dff25
commit 0e4cdfc566
7 changed files with 25 additions and 20 deletions

View File

@@ -9054,6 +9054,14 @@
"messageformat": "Donation succeeded, but we could not update your badge settings",
"description": "Toast shown when donation completes successfully but badge application fails"
},
"icu:Donations__BadgeApplicationFailed__Title": {
"messageformat": "Donation succeeded, but we could not update your badge settings",
"description": "Modal title shown when donation completes successfully but badge application fails"
},
"icu:Donations__BadgeApplicationFailed__Description": {
"messageformat": "To update your badge settings on your mobile device, go to Settings → Donate to Signal → Badges.",
"description": "Modal body text shown when badge application fails after successful donation"
},
"icu:Donations__DonationInterrupted": {
"messageformat": "Donation interrupted",
"description": "Title of the dialog shown when starting up if a donation had been started, and we've saved payment information, but the charge hasn't happened yet"

View File

@@ -45,6 +45,11 @@ export function DonationErrorModal(props: PropsType): JSX.Element {
body = i18n('icu:Donations__TimedOut__Description');
break;
}
case donationErrorTypeSchema.Enum.BadgeApplicationFailed: {
title = i18n('icu:Donations__BadgeApplicationFailed__Title');
body = i18n('icu:Donations__BadgeApplicationFailed__Description');
break;
}
default:
throw missingCaseError(props.errorType);

View File

@@ -628,6 +628,12 @@ export function PreferencesDonations({
i18n={i18n}
onClose={() => {
setIsSubmitted(false);
if (
workflow?.type === 'DONE' &&
lastError === donationErrorTypeSchema.Enum.BadgeApplicationFailed
) {
clearWorkflow();
}
updateLastError(undefined);
}}
/>
@@ -675,13 +681,13 @@ export function PreferencesDonations({
badge={donationBadge}
applyDonationBadge={applyDonationBadge}
onClose={(error?: Error) => {
clearWorkflow();
if (error) {
log.error('Badge application failed:', error.message);
showToast({
toastType: ToastType.DonationCompletedAndBadgeApplicationFailed,
});
updateLastError(
donationErrorTypeSchema.Enum.BadgeApplicationFailed
);
} else {
clearWorkflow();
showToast({
toastType: ToastType.DonationCompleted,
});

View File

@@ -41,10 +41,6 @@ function getToast(toastType: ToastType): AnyToast {
};
case ToastType.Blocked:
return { toastType: ToastType.Blocked };
case ToastType.DonationCompletedAndBadgeApplicationFailed:
return {
toastType: ToastType.DonationCompletedAndBadgeApplicationFailed,
};
case ToastType.BlockedGroup:
return { toastType: ToastType.BlockedGroup };
case ToastType.CallHistoryCleared:

View File

@@ -118,16 +118,6 @@ export function renderToast({
);
}
if (toastType === ToastType.DonationCompletedAndBadgeApplicationFailed) {
return (
<Toast onClose={hideToast}>
{i18n(
'icu:Donations__Toast__DonationCompletedAndBadgeApplicationFailed'
)}
</Toast>
);
}
if (toastType === ToastType.Blocked) {
return <Toast onClose={hideToast}>{i18n('icu:unblockToSend')}</Toast>;
}

View File

@@ -26,6 +26,8 @@ export const donationErrorTypeSchema = z.enum([
'PaymentDeclined',
// When it's been too long since the last step of the donation, and card wasn't charged
'TimedOut',
// When donation succeeds but badge application fails
'BadgeApplicationFailed',
]);
export type DonationErrorType = z.infer<typeof donationErrorTypeSchema>;

View File

@@ -8,7 +8,6 @@ export enum ToastType {
AlreadyRequestedToJoin = 'AlreadyRequestedToJoin',
AttachmentDownloadFailed = 'AttachmentDownloadFailed',
AttachmentDownloadStillInProgress = 'AttachmentDownloadStillInProgress',
DonationCompletedAndBadgeApplicationFailed = 'DonationCompletedAndBadgeApplicationFailed',
Blocked = 'Blocked',
BlockedGroup = 'BlockedGroup',
CallHistoryCleared = 'CallHistoryCleared',
@@ -104,7 +103,6 @@ export type AnyToast =
toastType: ToastType.AttachmentDownloadStillInProgress;
parameters: { count: number };
}
| { toastType: ToastType.DonationCompletedAndBadgeApplicationFailed }
| { toastType: ToastType.Blocked }
| { toastType: ToastType.BlockedGroup }
| { toastType: ToastType.CallHistoryCleared }