Introduce new DonationErrorModal component
Co-authored-by: ayumi-signal <143036029+ayumi-signal@users.noreply.github.com>
This commit is contained in:
parent
cb8edb4888
commit
e938e68c7d
6 changed files with 180 additions and 6 deletions
|
@ -8882,6 +8882,38 @@
|
|||
"messageformat": "Thank you for supporting Signal. Your contribution helps fuel the mission of protecting free expression and enabling secure global communication for millions around the world, through open source privacy technology. If you’re a resident of the United States, please retain this receipt for your tax records. Signal Technology Foundation is a tax-exempt nonprofit organization in the United States under section 501c3 of the Internal Revenue Code. Our Federal Tax ID is 82-4506840.",
|
||||
"description": "Footer text shown on donation receipts explaining tax deductibility and Signal's mission"
|
||||
},
|
||||
"icu:Donations__PaymentMethodDeclined": {
|
||||
"messageformat": "Payment method declined",
|
||||
"description": "Title of the dialog shown with the user's provided payment method has not worked"
|
||||
},
|
||||
"icu:Donations__PaymentMethodDeclined__Description": {
|
||||
"messageformat": "Try another payment method or contact your bank for more information.",
|
||||
"description": "An explanation for the 'payment declined' dialog"
|
||||
},
|
||||
"icu:Donations__ErrorProcessingDonation": {
|
||||
"messageformat": "Error processing donation",
|
||||
"description": "Title of the dialog shown when a user's donation didn't seem to complete"
|
||||
},
|
||||
"icu:Donations__ErrorProcessingDonation__Description": {
|
||||
"messageformat": "Try another payment method or contact your bank for more information.",
|
||||
"description": "An explanation for the 'error processing' dialog"
|
||||
},
|
||||
"icu:Donations__Failed3dsValidation": {
|
||||
"messageformat": "Verification Failed",
|
||||
"description": "Title of the dialog shown when something went wrong processing a user's 3ds verification with their bank"
|
||||
},
|
||||
"icu:Donations__Failed3dsValidation__Description": {
|
||||
"messageformat": "Additional verification step failed. Please try again.",
|
||||
"description": "An explanation for the 'verification failed' dialog"
|
||||
},
|
||||
"icu:Donations__GenericError": {
|
||||
"messageformat": "An error occurred with your donation",
|
||||
"description": "Title of the dialog shown when some unknown error has happened during a user's attempted donation"
|
||||
},
|
||||
"icu:Donations__GenericError__Description": {
|
||||
"messageformat": "Your donation might not have been processed. Click on “Donate to Signal” and then “Donation Receipts” to check your receipts and confirm.",
|
||||
"description": "An explanation for the 'error occurred' dialog"
|
||||
},
|
||||
"icu:Donations__Processing": {
|
||||
"messageformat": "Processing donation...",
|
||||
"description": "Explainer text for donation progress dialog"
|
||||
|
|
17
stylesheets/components/DonationErrorModal.scss
Normal file
17
stylesheets/components/DonationErrorModal.scss
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
@use '../mixins';
|
||||
|
||||
.DonationErrorModal__width-container {
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
// We include both for specificity
|
||||
.module-Modal__title.DonationErrorModal__title {
|
||||
@include mixins.font-title-medium;
|
||||
}
|
||||
|
||||
.DonationErrorModal__body_inner {
|
||||
@include mixins.font-body-2;
|
||||
}
|
|
@ -96,6 +96,7 @@
|
|||
@use 'components/DeleteMessagesModal.scss';
|
||||
@use 'components/DisappearingTimeDialog.scss';
|
||||
@use 'components/DisappearingTimerSelect.scss';
|
||||
@use 'components/DonationErrorModal.scss';
|
||||
@use 'components/DonationProgressModal.scss';
|
||||
@use 'components/DonationStillProcessingModal.scss';
|
||||
@use 'components/DonationVerificationModal.scss';
|
||||
|
|
58
ts/components/DonationError.stories.tsx
Normal file
58
ts/components/DonationError.stories.tsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import type { Meta } from '@storybook/react';
|
||||
import type { PropsType } from './DonationErrorModal';
|
||||
import { DonationErrorModal } from './DonationErrorModal';
|
||||
import { donationErrorTypeSchema } from '../types/Donations';
|
||||
|
||||
const { i18n } = window.SignalContext;
|
||||
|
||||
export default {
|
||||
title: 'Components/DonationErrorModal',
|
||||
} satisfies Meta<PropsType>;
|
||||
|
||||
const defaultProps = {
|
||||
i18n,
|
||||
onClose: action('onClose'),
|
||||
};
|
||||
|
||||
export function DonationProcessingError(): JSX.Element {
|
||||
return (
|
||||
<DonationErrorModal
|
||||
{...defaultProps}
|
||||
errorType={donationErrorTypeSchema.Enum.DonationProcessingError}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Failed3dsValidation(): JSX.Element {
|
||||
return (
|
||||
<DonationErrorModal
|
||||
{...defaultProps}
|
||||
errorType={donationErrorTypeSchema.Enum.Failed3dsValidation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function GeneralError(): JSX.Element {
|
||||
return (
|
||||
<DonationErrorModal
|
||||
{...defaultProps}
|
||||
errorType={donationErrorTypeSchema.Enum.GeneralError}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function PaymentDeclined(): JSX.Element {
|
||||
return (
|
||||
<DonationErrorModal
|
||||
{...defaultProps}
|
||||
errorType={donationErrorTypeSchema.Enum.PaymentDeclined}
|
||||
/>
|
||||
);
|
||||
}
|
68
ts/components/DonationErrorModal.tsx
Normal file
68
ts/components/DonationErrorModal.tsx
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
import { donationErrorTypeSchema } from '../types/Donations';
|
||||
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { DonationErrorType } from '../types/Donations';
|
||||
import { Button } from './Button';
|
||||
import { Modal } from './Modal';
|
||||
|
||||
export type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
onClose: () => void;
|
||||
errorType: DonationErrorType;
|
||||
};
|
||||
|
||||
export function DonationErrorModal(props: PropsType): JSX.Element {
|
||||
const { i18n, onClose } = props;
|
||||
|
||||
let title: string;
|
||||
let body: ReactNode;
|
||||
|
||||
switch (props.errorType) {
|
||||
case donationErrorTypeSchema.Enum.DonationProcessingError: {
|
||||
title = i18n('icu:Donations__ErrorProcessingDonation');
|
||||
body = i18n('icu:Donations__ErrorProcessingDonation__Description');
|
||||
break;
|
||||
}
|
||||
case donationErrorTypeSchema.Enum.Failed3dsValidation: {
|
||||
title = i18n('icu:Donations__Failed3dsValidation');
|
||||
body = i18n('icu:Donations__Failed3dsValidation__Description');
|
||||
break;
|
||||
}
|
||||
case donationErrorTypeSchema.Enum.GeneralError: {
|
||||
title = i18n('icu:Donations__GenericError');
|
||||
body = i18n('icu:Donations__GenericError__Description');
|
||||
break;
|
||||
}
|
||||
case donationErrorTypeSchema.Enum.PaymentDeclined: {
|
||||
title = i18n('icu:Donations__PaymentMethodDeclined');
|
||||
body = i18n('icu:Donations__PaymentMethodDeclined__Description');
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw missingCaseError(props.errorType);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
i18n={i18n}
|
||||
modalFooter={
|
||||
<Button onClick={onClose}>{i18n('icu:Confirmation--confirm')}</Button>
|
||||
}
|
||||
hasXButton
|
||||
moduleClassName="DonationErrorModal"
|
||||
modalName="DonationErrorModal"
|
||||
onClose={onClose}
|
||||
title={title}
|
||||
>
|
||||
{body}
|
||||
</Modal>
|
||||
);
|
||||
}
|
|
@ -13,16 +13,14 @@ export const donationStateSchema = z.enum([
|
|||
]);
|
||||
|
||||
export const donationErrorTypeSchema = z.enum([
|
||||
// Any 4xx error when adding payment method or confirming intent
|
||||
'PaymentDeclined',
|
||||
// Only used if we can't support 3DS validation for our first release
|
||||
'CardNotSupported',
|
||||
// Used if the user is redirected back from validation, but continuing forward fails
|
||||
'Failed3dsValidation',
|
||||
// Any other HTTPError during the process
|
||||
'DonationProcessingError',
|
||||
// Used if the user is redirected back from validation, but continuing forward fails
|
||||
'Failed3dsValidation',
|
||||
// Any other error
|
||||
'GeneralError',
|
||||
// Any 4xx error when adding payment method or confirming intent
|
||||
'PaymentDeclined',
|
||||
]);
|
||||
export type DonationErrorType = z.infer<typeof donationErrorTypeSchema>;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue