Fix donations processing dialog on retry and add onEnter handler to form inputs

This commit is contained in:
ayumi-signal
2025-08-21 14:41:30 -07:00
committed by GitHub
parent 332936a938
commit 277ea13093
9 changed files with 80 additions and 18 deletions

View File

@@ -36,7 +36,6 @@ export function DonationProgressModal(props: PropsType): JSX.Element {
modalName="DonationProgressModal"
noEscapeClose
noMouseClose
onClose={() => undefined}
>
<SpinnerV2 size={58} strokeWidth={8} />
<div className="DonationProgressModal__text">

View File

@@ -614,22 +614,45 @@ function CardForm({
setCardCvcError(formResult.cardCvc.error ?? null);
const cardDetail = cardFormToCardDetail(formResult);
if (cardDetail == null) {
if (
cardDetail == null ||
formResult.cardNumber.error ||
formResult.cardExpiration.error ||
formResult.cardCvc.error
) {
return;
}
onSubmit(cardDetail);
}, [cardCvc, cardExpiration, cardNumber, onSubmit]);
const isDonateDisabled =
disabled ||
!isOnline ||
cardNumber === '' ||
cardExpiration === '' ||
cardCvc === '' ||
cardNumberError != null ||
cardExpirationError != null ||
cardCvcError != null;
const isDonateDisabled = useMemo(
() =>
disabled ||
!isOnline ||
cardNumber === '' ||
cardExpiration === '' ||
cardCvc === '' ||
cardNumberError != null ||
cardExpirationError != null ||
cardCvcError != null,
[
cardCvc,
cardCvcError,
cardExpiration,
cardExpirationError,
cardNumber,
cardNumberError,
disabled,
isOnline,
]
);
const handleInputEnterKey = useCallback(() => {
if (!isDonateDisabled) {
handleDonateClicked();
}
}, [handleDonateClicked, isDonateDisabled]);
const donateButton = (
<Button
@@ -674,6 +697,7 @@ function CardForm({
onValueChange={handleCardNumberChange}
maxInputLength={cardFormSettings.cardNumber.maxInputLength}
onBlur={handleCardNumberBlur}
onEnter={handleInputEnterKey}
/>
{cardNumberError != null && (
<div className="DonationCardForm_FieldError">
@@ -697,6 +721,7 @@ function CardForm({
value={cardExpiration}
onValueChange={handleCardExpirationChange}
onBlur={handleCardExpirationBlur}
onEnter={handleInputEnterKey}
/>
{cardExpirationError && (
<div className="DonationCardForm_FieldError">
@@ -720,6 +745,7 @@ function CardForm({
onValueChange={handleCardCvcChange}
maxInputLength={cardFormSettings.cardCvc.maxInputLength}
onBlur={handleCardCvcBlur}
onEnter={handleInputEnterKey}
/>
{cardCvcError && (
<div className="DonationCardForm_FieldError">

View File

@@ -597,7 +597,7 @@ export function PreferencesDonations({
);
useEffect(() => {
if (!workflow || lastError) {
if (lastError) {
setIsSubmitted(false);
}
@@ -636,6 +636,7 @@ export function PreferencesDonations({
errorType={lastError}
i18n={i18n}
onClose={() => {
setIsSubmitted(false);
updateLastError(undefined);
}}
/>

View File

@@ -14,6 +14,7 @@ export default {
onValueChange: action('onValueChange'),
maxInputLength: 3,
onBlur: action('onBlur'),
onEnter: action('onEnter'),
},
} satisfies ComponentMeta<DonateInputCardCvcProps>;

View File

@@ -1,6 +1,6 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { FormEvent } from 'react';
import type { FormEvent, KeyboardEvent } from 'react';
import React, { memo, useCallback, useRef } from 'react';
import { CC_CVC_FORMATTER, useInputMask } from '../../../hooks/useInputMask';
import type { LocalizerType } from '../../../types/I18N';
@@ -30,12 +30,13 @@ export type DonateInputCardCvcProps = Readonly<{
onValueChange: (newValue: string) => void;
maxInputLength: number;
onBlur?: () => void;
onEnter?: () => void;
}>;
export const DonateInputCardCvc = memo(function DonateInputCardCvc(
props: DonateInputCardCvcProps
) {
const { onValueChange } = props;
const { onEnter, onValueChange } = props;
const inputRef = useRef<HTMLInputElement>(null);
useInputMask(inputRef, CC_CVC_FORMATTER);
@@ -47,6 +48,15 @@ export const DonateInputCardCvc = memo(function DonateInputCardCvc(
[onValueChange]
);
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
if (onEnter && event.key === 'Enter') {
onEnter();
}
},
[onEnter]
);
return (
<input
ref={inputRef}
@@ -59,6 +69,7 @@ export const DonateInputCardCvc = memo(function DonateInputCardCvc(
value={props.value}
onInput={handleInput}
onBlur={props.onBlur}
onKeyDown={handleKeyDown}
/>
);
});

View File

@@ -13,6 +13,7 @@ export default {
value: '',
onValueChange: action('onValueChange'),
onBlur: action('onBlur'),
onEnter: action('onEnter'),
},
} satisfies ComponentMeta<DonateInputCardExpProps>;

View File

@@ -1,6 +1,6 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { FormEvent } from 'react';
import type { FormEvent, KeyboardEvent } from 'react';
import React, { memo, useCallback, useRef } from 'react';
import { CC_EXP_FORMATTER, useInputMask } from '../../../hooks/useInputMask';
import { CardExpirationError } from '../../../types/DonationsCardForm';
@@ -40,12 +40,13 @@ export type DonateInputCardExpProps = Readonly<{
value: string;
onValueChange: (newValue: string) => void;
onBlur?: () => void;
onEnter?: () => void;
}>;
export const DonateInputCardExp = memo(function DonateInputCardExp(
props: DonateInputCardExpProps
) {
const { onValueChange } = props;
const { onEnter, onValueChange } = props;
const inputRef = useRef<HTMLInputElement>(null);
useInputMask(inputRef, CC_EXP_FORMATTER);
@@ -57,6 +58,15 @@ export const DonateInputCardExp = memo(function DonateInputCardExp(
[onValueChange]
);
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
if (onEnter && event.key === 'Enter') {
onEnter();
}
},
[onEnter]
);
return (
<input
ref={inputRef}
@@ -68,6 +78,7 @@ export const DonateInputCardExp = memo(function DonateInputCardExp(
value={props.value}
onInput={handleInput}
onBlur={props.onBlur}
onKeyDown={handleKeyDown}
/>
);
});

View File

@@ -13,6 +13,7 @@ export default {
value: '',
onValueChange: action('onValueChange'),
onBlur: action('onBlur'),
onEnter: action('onEnter'),
maxInputLength: 19,
},
} satisfies ComponentMeta<DonateInputCardNumberProps>;

View File

@@ -1,6 +1,6 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { FormEvent } from 'react';
import type { FormEvent, KeyboardEvent } from 'react';
import React, { memo, useCallback, useRef } from 'react';
import { CC_NUMBER_FORMATTER, useInputMask } from '../../../hooks/useInputMask';
import type { LocalizerType } from '../../../types/I18N';
@@ -28,12 +28,13 @@ export type DonateInputCardNumberProps = Readonly<{
onValueChange: (newValue: string) => void;
maxInputLength: number;
onBlur?: () => void;
onEnter?: () => void;
}>;
export const DonateInputCardNumber = memo(function DonateInputCardNumber(
props: DonateInputCardNumberProps
) {
const { onValueChange } = props;
const { onEnter, onValueChange } = props;
const inputRef = useRef<HTMLInputElement>(null);
useInputMask(inputRef, CC_NUMBER_FORMATTER);
@@ -45,6 +46,15 @@ export const DonateInputCardNumber = memo(function DonateInputCardNumber(
[onValueChange]
);
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
if (onEnter && event.key === 'Enter') {
onEnter();
}
},
[onEnter]
);
return (
<input
ref={inputRef}
@@ -57,6 +67,7 @@ export const DonateInputCardNumber = memo(function DonateInputCardNumber(
value={props.value}
onInput={handleInput}
onBlur={props.onBlur}
onKeyDown={handleKeyDown}
/>
);
});