mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-20 02:08:57 +00:00
Donations: Show confirmation toast on startup at INTENT_METHOD
Co-authored-by: ayumi-signal <143036029+ayumi-signal@users.noreply.github.com>
This commit is contained in:
@@ -8926,6 +8926,22 @@
|
|||||||
"messageformat": "You have a donation in progress that needs additional verification.",
|
"messageformat": "You have a donation in progress that needs additional verification.",
|
||||||
"description": "Shown when the user is not on the Preferences/Donations screen, and donation verification is needed. Like when resuming from startup."
|
"description": "Shown when the user is not on the Preferences/Donations screen, and donation verification is needed. Like when resuming from startup."
|
||||||
},
|
},
|
||||||
|
"icu:Donations__Toast__ConfirmationNeeded": {
|
||||||
|
"messageformat": "You have a donation in progress that requires confirmation.",
|
||||||
|
"description": "Shown when the user is not on the Preferences/Donations screen, and donation verification is needed. Like when resuming from startup."
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"icu:Donations__DonationInterrupted__Description": {
|
||||||
|
"messageformat": "Your card was not charged. Do you want to retry the donation?",
|
||||||
|
"description": "An explanation for the 'donation interrupted' dialog"
|
||||||
|
},
|
||||||
|
"icu:Donations__DonationInterrupted__RetryButton": {
|
||||||
|
"messageformat": "Try again",
|
||||||
|
"description": "The button in the 'donation interrupted' dialog which allows the user to move forward with the donation."
|
||||||
|
},
|
||||||
"icu:Donations__PaymentMethodDeclined": {
|
"icu:Donations__PaymentMethodDeclined": {
|
||||||
"messageformat": "Payment method declined",
|
"messageformat": "Payment method declined",
|
||||||
"description": "Title of the dialog shown with the user's provided payment method has not worked"
|
"description": "Title of the dialog shown with the user's provided payment method has not worked"
|
||||||
|
|||||||
17
stylesheets/components/DonationInterruptedModal.scss
Normal file
17
stylesheets/components/DonationInterruptedModal.scss
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
@use '../mixins';
|
||||||
|
|
||||||
|
.DonationInterruptedModal__width-container {
|
||||||
|
max-width: 420px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We include both for specificity
|
||||||
|
.module-Modal__title.DonationInterruptedModal__title {
|
||||||
|
@include mixins.font-title-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DonationInterruptedModal__body_inner {
|
||||||
|
@include mixins.font-body-2;
|
||||||
|
}
|
||||||
@@ -98,6 +98,7 @@
|
|||||||
@use 'components/DisappearingTimerSelect.scss';
|
@use 'components/DisappearingTimerSelect.scss';
|
||||||
@use 'components/DonationErrorModal.scss';
|
@use 'components/DonationErrorModal.scss';
|
||||||
@use 'components/DonationForm.scss';
|
@use 'components/DonationForm.scss';
|
||||||
|
@use 'components/DonationInterruptedModal.scss';
|
||||||
@use 'components/DonationProgressModal.scss';
|
@use 'components/DonationProgressModal.scss';
|
||||||
@use 'components/DonationStillProcessingModal.scss';
|
@use 'components/DonationStillProcessingModal.scss';
|
||||||
@use 'components/DonationVerificationModal.scss';
|
@use 'components/DonationVerificationModal.scss';
|
||||||
|
|||||||
26
ts/components/DonationInterruptedModal.stories.tsx
Normal file
26
ts/components/DonationInterruptedModal.stories.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// 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 './DonationInterruptedModal';
|
||||||
|
import { DonationInterruptedModal } from './DonationInterruptedModal';
|
||||||
|
|
||||||
|
const { i18n } = window.SignalContext;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/DonationInterruptedModal',
|
||||||
|
} satisfies Meta<PropsType>;
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
i18n,
|
||||||
|
onCancelDonation: action('onCancelDonation'),
|
||||||
|
onRetryDonation: action('onRetryDonation'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Default(): JSX.Element {
|
||||||
|
return <DonationInterruptedModal {...defaultProps} />;
|
||||||
|
}
|
||||||
47
ts/components/DonationInterruptedModal.tsx
Normal file
47
ts/components/DonationInterruptedModal.tsx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import type { LocalizerType } from '../types/Util';
|
||||||
|
import { Modal } from './Modal';
|
||||||
|
import { Button, ButtonVariant } from './Button';
|
||||||
|
|
||||||
|
export type PropsType = {
|
||||||
|
i18n: LocalizerType;
|
||||||
|
onCancelDonation: () => unknown;
|
||||||
|
onRetryDonation: () => unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function DonationInterruptedModal(props: PropsType): JSX.Element {
|
||||||
|
const { i18n, onCancelDonation, onRetryDonation } = props;
|
||||||
|
|
||||||
|
const footer = (
|
||||||
|
<>
|
||||||
|
<Button variant={ButtonVariant.Secondary} onClick={onCancelDonation}>
|
||||||
|
{i18n('icu:cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
onRetryDonation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18n('icu:Donations__DonationInterrupted__RetryButton')}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
i18n={i18n}
|
||||||
|
modalFooter={footer}
|
||||||
|
moduleClassName="DonationInterruptedModal"
|
||||||
|
modalName="DonationInterruptedModal"
|
||||||
|
noMouseClose
|
||||||
|
onClose={onCancelDonation}
|
||||||
|
title={i18n('icu:Donations__DonationInterrupted')}
|
||||||
|
>
|
||||||
|
{i18n('icu:Donations__DonationInterrupted__Description')}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -219,12 +219,14 @@ function renderDonationsPane(props: {
|
|||||||
contentsRef={props.contentsRef}
|
contentsRef={props.contentsRef}
|
||||||
clearWorkflow={action('clearWorkflow')}
|
clearWorkflow={action('clearWorkflow')}
|
||||||
initialCurrency="USD"
|
initialCurrency="USD"
|
||||||
|
resumeWorkflow={action('resumeWorkflow')}
|
||||||
isStaging
|
isStaging
|
||||||
page={props.page}
|
page={props.page}
|
||||||
setPage={props.setPage}
|
setPage={props.setPage}
|
||||||
submitDonation={action('submitDonation')}
|
submitDonation={action('submitDonation')}
|
||||||
lastError={undefined}
|
lastError={undefined}
|
||||||
workflow={undefined}
|
workflow={undefined}
|
||||||
|
didResumeWorkflowAtStartup={false}
|
||||||
badge={undefined}
|
badge={undefined}
|
||||||
color={props.me.color}
|
color={props.me.color}
|
||||||
firstName={props.me.firstName}
|
firstName={props.me.firstName}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import type { SubmitDonationType } from '../state/ducks/donations';
|
|||||||
import { getHumanDonationAmount } from '../util/currency';
|
import { getHumanDonationAmount } from '../util/currency';
|
||||||
import { Avatar, AvatarSize } from './Avatar';
|
import { Avatar, AvatarSize } from './Avatar';
|
||||||
import type { BadgeType } from '../badges/types';
|
import type { BadgeType } from '../badges/types';
|
||||||
|
import { DonationInterruptedModal } from './DonationInterruptedModal';
|
||||||
import { DonationErrorModal } from './DonationErrorModal';
|
import { DonationErrorModal } from './DonationErrorModal';
|
||||||
import { DonationVerificationModal } from './DonationVerificationModal';
|
import { DonationVerificationModal } from './DonationVerificationModal';
|
||||||
import { DonationProgressModal } from './DonationProgressModal';
|
import { DonationProgressModal } from './DonationProgressModal';
|
||||||
@@ -50,6 +51,7 @@ export type PropsDataType = {
|
|||||||
initialCurrency: string;
|
initialCurrency: string;
|
||||||
isStaging: boolean;
|
isStaging: boolean;
|
||||||
page: SettingsPage;
|
page: SettingsPage;
|
||||||
|
didResumeWorkflowAtStartup: boolean;
|
||||||
lastError: DonationErrorType | undefined;
|
lastError: DonationErrorType | undefined;
|
||||||
workflow: DonationWorkflow | undefined;
|
workflow: DonationWorkflow | undefined;
|
||||||
badge: BadgeType | undefined;
|
badge: BadgeType | undefined;
|
||||||
@@ -69,12 +71,13 @@ export type PropsDataType = {
|
|||||||
receipt: DonationReceipt,
|
receipt: DonationReceipt,
|
||||||
i18n: LocalizerType
|
i18n: LocalizerType
|
||||||
) => Promise<Blob>;
|
) => Promise<Blob>;
|
||||||
showToast: (toast: AnyToast) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type PropsActionType = {
|
type PropsActionType = {
|
||||||
clearWorkflow: () => void;
|
clearWorkflow: () => void;
|
||||||
|
resumeWorkflow: () => void;
|
||||||
setPage: (page: SettingsPage) => void;
|
setPage: (page: SettingsPage) => void;
|
||||||
|
showToast: (toast: AnyToast) => void;
|
||||||
submitDonation: (payload: SubmitDonationType) => void;
|
submitDonation: (payload: SubmitDonationType) => void;
|
||||||
updateLastError: (error: DonationErrorType | undefined) => void;
|
updateLastError: (error: DonationErrorType | undefined) => void;
|
||||||
};
|
};
|
||||||
@@ -86,7 +89,10 @@ type DonationPage =
|
|||||||
| SettingsPage.DonationsDonateFlow
|
| SettingsPage.DonationsDonateFlow
|
||||||
| SettingsPage.DonationsReceiptList;
|
| SettingsPage.DonationsReceiptList;
|
||||||
|
|
||||||
type PreferencesHomeProps = Omit<PropsType, 'badge' | 'theme'> & {
|
type PreferencesHomeProps = Pick<
|
||||||
|
PropsType,
|
||||||
|
'contentsRef' | 'i18n' | 'setPage' | 'isStaging' | 'donationReceipts'
|
||||||
|
> & {
|
||||||
navigateToPage: (newPage: SettingsPage) => void;
|
navigateToPage: (newPage: SettingsPage) => void;
|
||||||
renderDonationHero: () => JSX.Element;
|
renderDonationHero: () => JSX.Element;
|
||||||
};
|
};
|
||||||
@@ -457,8 +463,10 @@ export function PreferencesDonations({
|
|||||||
isStaging,
|
isStaging,
|
||||||
page,
|
page,
|
||||||
workflow,
|
workflow,
|
||||||
|
didResumeWorkflowAtStartup,
|
||||||
lastError,
|
lastError,
|
||||||
clearWorkflow,
|
clearWorkflow,
|
||||||
|
resumeWorkflow,
|
||||||
setPage,
|
setPage,
|
||||||
submitDonation,
|
submitDonation,
|
||||||
badge,
|
badge,
|
||||||
@@ -526,6 +534,23 @@ export function PreferencesDonations({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
} else if (
|
||||||
|
didResumeWorkflowAtStartup &&
|
||||||
|
workflow?.type === donationStateSchema.Enum.INTENT_METHOD
|
||||||
|
) {
|
||||||
|
dialog = (
|
||||||
|
<DonationInterruptedModal
|
||||||
|
i18n={i18n}
|
||||||
|
onCancelDonation={() => {
|
||||||
|
clearWorkflow();
|
||||||
|
setPage(SettingsPage.Donations);
|
||||||
|
showToast({ toastType: ToastType.DonationCancelled });
|
||||||
|
}}
|
||||||
|
onRetryDonation={() => {
|
||||||
|
resumeWorkflow();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else if (workflow?.type === donationStateSchema.Enum.INTENT_REDIRECT) {
|
} else if (workflow?.type === donationStateSchema.Enum.INTENT_REDIRECT) {
|
||||||
dialog = (
|
dialog = (
|
||||||
<DonationVerificationModal
|
<DonationVerificationModal
|
||||||
@@ -604,26 +629,11 @@ export function PreferencesDonations({
|
|||||||
<DonationsHome
|
<DonationsHome
|
||||||
contentsRef={contentsRef}
|
contentsRef={contentsRef}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
color={color}
|
|
||||||
firstName={firstName}
|
|
||||||
profileAvatarUrl={profileAvatarUrl}
|
|
||||||
navigateToPage={navigateToPage}
|
navigateToPage={navigateToPage}
|
||||||
donationReceipts={donationReceipts}
|
donationReceipts={donationReceipts}
|
||||||
donationAmountsConfig={donationAmountsConfig}
|
|
||||||
validCurrencies={validCurrencies}
|
|
||||||
saveAttachmentToDisk={saveAttachmentToDisk}
|
|
||||||
generateDonationReceiptBlob={generateDonationReceiptBlob}
|
|
||||||
showToast={showToast}
|
|
||||||
isStaging={isStaging}
|
isStaging={isStaging}
|
||||||
initialCurrency={initialCurrency}
|
|
||||||
page={page}
|
|
||||||
lastError={lastError}
|
|
||||||
workflow={workflow}
|
|
||||||
clearWorkflow={clearWorkflow}
|
|
||||||
renderDonationHero={renderDonationHero}
|
renderDonationHero={renderDonationHero}
|
||||||
setPage={setPage}
|
setPage={setPage}
|
||||||
submitDonation={submitDonation}
|
|
||||||
updateLastError={updateLastError}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (page === SettingsPage.DonationsReceiptList) {
|
} else if (page === SettingsPage.DonationsReceiptList) {
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ function getToast(toastType: ToastType): AnyToast {
|
|||||||
return { toastType: ToastType.DonationCancelled };
|
return { toastType: ToastType.DonationCancelled };
|
||||||
case ToastType.DonationCompleted:
|
case ToastType.DonationCompleted:
|
||||||
return { toastType: ToastType.DonationCompleted };
|
return { toastType: ToastType.DonationCompleted };
|
||||||
|
case ToastType.DonationConfirmationNeeded:
|
||||||
|
return { toastType: ToastType.DonationConfirmationNeeded };
|
||||||
case ToastType.DonationError:
|
case ToastType.DonationError:
|
||||||
return { toastType: ToastType.DonationError };
|
return { toastType: ToastType.DonationError };
|
||||||
case ToastType.DonationProcessing:
|
case ToastType.DonationProcessing:
|
||||||
|
|||||||
@@ -337,11 +337,15 @@ export function renderToast({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
toastType === ToastType.DonationConfirmationNeeded ||
|
||||||
toastType === ToastType.DonationError ||
|
toastType === ToastType.DonationError ||
|
||||||
toastType === ToastType.DonationVerificationFailed ||
|
toastType === ToastType.DonationVerificationFailed ||
|
||||||
toastType === ToastType.DonationVerificationNeeded
|
toastType === ToastType.DonationVerificationNeeded
|
||||||
) {
|
) {
|
||||||
const mapping = {
|
const mapping = {
|
||||||
|
[ToastType.DonationConfirmationNeeded]: i18n(
|
||||||
|
'icu:Donations__Toast__ConfirmationNeeded'
|
||||||
|
),
|
||||||
[ToastType.DonationError]: i18n('icu:Donations__Toast__Error'),
|
[ToastType.DonationError]: i18n('icu:Donations__Toast__Error'),
|
||||||
[ToastType.DonationVerificationFailed]: i18n(
|
[ToastType.DonationVerificationFailed]: i18n(
|
||||||
'icu:Donations__Toast__VerificationFailed'
|
'icu:Donations__Toast__VerificationFailed'
|
||||||
@@ -355,6 +359,7 @@ export function renderToast({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Toast
|
<Toast
|
||||||
|
autoDismissDisabled
|
||||||
onClose={hideToast}
|
onClose={hideToast}
|
||||||
toastAction={{
|
toastAction={{
|
||||||
label: i18n('icu:view'),
|
label: i18n('icu:view'),
|
||||||
|
|||||||
@@ -85,7 +85,24 @@ export async function initialize(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (didResumeWorkflowAtStartup() && !isDonationPageVisible()) {
|
const shouldShowToast =
|
||||||
|
didResumeWorkflowAtStartup() && !isDonationPageVisible();
|
||||||
|
|
||||||
|
if (workflow.type === donationStateSchema.Enum.INTENT_METHOD) {
|
||||||
|
if (shouldShowToast) {
|
||||||
|
log.info(
|
||||||
|
'initialize: Showing confirmation toast, workflow is at INTENT_METHOD.'
|
||||||
|
);
|
||||||
|
window.reduxActions.toast.showToast({
|
||||||
|
toastType: ToastType.DonationConfirmationNeeded,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that we are not starting the workflow here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldShowToast) {
|
||||||
log.info(
|
log.info(
|
||||||
'initialize: We resumed at startup and donation page not visible. Showing processing toast.'
|
'initialize: We resumed at startup and donation page not visible. Showing processing toast.'
|
||||||
);
|
);
|
||||||
@@ -97,7 +114,7 @@ export async function initialize(): Promise<void> {
|
|||||||
await _runDonationWorkflow();
|
await _runDonationWorkflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are the four moments the user provides input to the donation workflow. So,
|
// These are the five moments the user provides input to the donation workflow. So,
|
||||||
// UI calls these methods directly; everything else happens automatically.
|
// UI calls these methods directly; everything else happens automatically.
|
||||||
|
|
||||||
export async function startDonation({
|
export async function startDonation({
|
||||||
@@ -169,6 +186,15 @@ export async function clearDonation(): Promise<void> {
|
|||||||
await _saveWorkflow(undefined);
|
await _saveWorkflow(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function resumeDonation(): Promise<void> {
|
||||||
|
const existing = _getWorkflowFromRedux();
|
||||||
|
if (!existing) {
|
||||||
|
throw new Error('resumeDonation: Cannot finish nonexistent workflow!');
|
||||||
|
}
|
||||||
|
|
||||||
|
await _saveAndRunWorkflow(existing);
|
||||||
|
}
|
||||||
|
|
||||||
// For testing
|
// For testing
|
||||||
|
|
||||||
export async function _internalDoDonation({
|
export async function _internalDoDonation({
|
||||||
@@ -295,6 +321,13 @@ export async function _runDonationWorkflow(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type === donationStateSchema.Enum.INTENT_METHOD) {
|
if (type === donationStateSchema.Enum.INTENT_METHOD) {
|
||||||
|
if (didResumeWorkflowAtStartup()) {
|
||||||
|
log.info(
|
||||||
|
`${logId}: Resumed after startup and haven't charged payment method. Waiting for user confirmation.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log.info(`${logId}: Attempting to confirm payment`);
|
log.info(`${logId}: Attempting to confirm payment`);
|
||||||
updated = await _confirmPayment(existing);
|
updated = await _confirmPayment(existing);
|
||||||
// continuing
|
// continuing
|
||||||
|
|||||||
@@ -114,6 +114,27 @@ function setDidResume(didResume: boolean): SetDidResumeAction {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resumeWorkflow(): ThunkAction<
|
||||||
|
void,
|
||||||
|
RootStateType,
|
||||||
|
unknown,
|
||||||
|
SetDidResumeAction
|
||||||
|
> {
|
||||||
|
return async dispatch => {
|
||||||
|
try {
|
||||||
|
dispatch({
|
||||||
|
type: SET_DID_RESUME,
|
||||||
|
payload: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await donations.resumeDonation();
|
||||||
|
} catch (error) {
|
||||||
|
log.error('Error resuming workflow', Errors.toLogFormat(error));
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type SubmitDonationType = ReadonlyDeep<{
|
export type SubmitDonationType = ReadonlyDeep<{
|
||||||
currencyType: string;
|
currencyType: string;
|
||||||
paymentAmount: StripeDonationAmount;
|
paymentAmount: StripeDonationAmount;
|
||||||
@@ -132,7 +153,7 @@ function submitDonation({
|
|||||||
> {
|
> {
|
||||||
return async (_dispatch, getState) => {
|
return async (_dispatch, getState) => {
|
||||||
if (!isStagingServer()) {
|
if (!isStagingServer()) {
|
||||||
log.error('internalAddDonationReceipt: Only available on staging server');
|
log.error('submitDonation: Only available on staging server');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,6 +212,7 @@ export const actions = {
|
|||||||
clearWorkflow,
|
clearWorkflow,
|
||||||
internalAddDonationReceipt,
|
internalAddDonationReceipt,
|
||||||
setDidResume,
|
setDidResume,
|
||||||
|
resumeWorkflow,
|
||||||
submitDonation,
|
submitDonation,
|
||||||
updateLastError,
|
updateLastError,
|
||||||
updateWorkflow,
|
updateWorkflow,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const SmartPreferencesDonations = memo(
|
|||||||
const theme = useSelector(getTheme);
|
const theme = useSelector(getTheme);
|
||||||
|
|
||||||
const donationsState = useSelector((state: StateType) => state.donations);
|
const donationsState = useSelector((state: StateType) => state.donations);
|
||||||
const { clearWorkflow, submitDonation, updateLastError } =
|
const { clearWorkflow, resumeWorkflow, submitDonation, updateLastError } =
|
||||||
useDonationsActions();
|
useDonationsActions();
|
||||||
|
|
||||||
const ourNumber = useSelector(getUserNumber);
|
const ourNumber = useSelector(getUserNumber);
|
||||||
@@ -93,9 +93,11 @@ export const SmartPreferencesDonations = memo(
|
|||||||
initialCurrency={initialCurrency}
|
initialCurrency={initialCurrency}
|
||||||
isStaging={isStaging}
|
isStaging={isStaging}
|
||||||
page={page}
|
page={page}
|
||||||
|
didResumeWorkflowAtStartup={donationsState.didResumeWorkflowAtStartup}
|
||||||
lastError={donationsState.lastError}
|
lastError={donationsState.lastError}
|
||||||
workflow={donationsState.currentWorkflow}
|
workflow={donationsState.currentWorkflow}
|
||||||
clearWorkflow={clearWorkflow}
|
clearWorkflow={clearWorkflow}
|
||||||
|
resumeWorkflow={resumeWorkflow}
|
||||||
updateLastError={updateLastError}
|
updateLastError={updateLastError}
|
||||||
submitDonation={submitDonation}
|
submitDonation={submitDonation}
|
||||||
setPage={setPage}
|
setPage={setPage}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export enum ToastType {
|
|||||||
DeleteForEveryoneFailed = 'DeleteForEveryoneFailed',
|
DeleteForEveryoneFailed = 'DeleteForEveryoneFailed',
|
||||||
DonationCancelled = 'DonationCancelled',
|
DonationCancelled = 'DonationCancelled',
|
||||||
DonationCompleted = 'DonationCompleted',
|
DonationCompleted = 'DonationCompleted',
|
||||||
|
DonationConfirmationNeeded = 'DonationConfirmationNeeded',
|
||||||
DonationError = 'DonationError',
|
DonationError = 'DonationError',
|
||||||
DonationProcessing = 'DonationProcessing',
|
DonationProcessing = 'DonationProcessing',
|
||||||
DonationVerificationNeeded = 'DonationVerificationNeeded',
|
DonationVerificationNeeded = 'DonationVerificationNeeded',
|
||||||
@@ -128,6 +129,7 @@ export type AnyToast =
|
|||||||
| { toastType: ToastType.DeleteForEveryoneFailed }
|
| { toastType: ToastType.DeleteForEveryoneFailed }
|
||||||
| { toastType: ToastType.DonationCancelled }
|
| { toastType: ToastType.DonationCancelled }
|
||||||
| { toastType: ToastType.DonationCompleted }
|
| { toastType: ToastType.DonationCompleted }
|
||||||
|
| { toastType: ToastType.DonationConfirmationNeeded }
|
||||||
| { toastType: ToastType.DonationError }
|
| { toastType: ToastType.DonationError }
|
||||||
| { toastType: ToastType.DonationProcessing }
|
| { toastType: ToastType.DonationProcessing }
|
||||||
| { toastType: ToastType.DonationVerificationFailed }
|
| { toastType: ToastType.DonationVerificationFailed }
|
||||||
|
|||||||
Reference in New Issue
Block a user