Payment Modal: Various fixes (#2124)

This commit is contained in:
Alexander Zinchuk 2022-11-10 18:28:05 +04:00
parent f272f63afc
commit d10df1a705
12 changed files with 59 additions and 35 deletions

View File

@ -166,6 +166,7 @@ export interface ApiPoll {
export type ApiInputInvoice = {
chatId: string;
messageId: number;
isExtendedMedia?: boolean;
} | {
slug: string;
};

View File

@ -1,6 +1,6 @@
const VISA = /^4[0-9]{12}(?:[0-9]{1,3})?$/;
const MASTERCARD1 = /^5[1-5][0-9]{11,14}$/;
const MASTERCARD2 = /^2[2-7][0-9]{11,14}$/;
const VISA = /^4\d/;
const MASTERCARD1 = /^5[1-5]/;
const MASTERCARD2 = /^2[2-7]\d{2}/;
const MIR = /^220[0-4]/;
export enum CardType {

View File

@ -1,5 +1,5 @@
import type { FC } from '../../../lib/teact/teact';
import React, { memo, useCallback, useEffect } from '../../../lib/teact/teact';
import React, { memo } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import type { ApiMessage } from '../../../api/types';
@ -7,7 +7,6 @@ import type { ApiMessage } from '../../../api/types';
import { IS_TOUCH_ENV } from '../../../util/environment';
import { selectChatMessage, selectCurrentMessageList } from '../../../global/selectors';
import useMouseInside from '../../../hooks/useMouseInside';
import useFlag from '../../../hooks/useFlag';
import Menu from '../../ui/Menu';
import Button from '../../ui/Button';
@ -31,16 +30,6 @@ const BotKeyboardMenu: FC<OwnProps & StateProps> = ({
const [handleMouseEnter, handleMouseLeave] = useMouseInside(isOpen, onClose);
const { isKeyboardSingleUse } = message || {};
const [forceOpen, markForceOpen, unmarkForceOpen] = useFlag(true);
const handleClose = useCallback(() => {
unmarkForceOpen();
onClose();
}, [onClose, unmarkForceOpen]);
useEffect(() => {
markForceOpen();
}, [markForceOpen, message?.keyboardButtons]);
if (!message || !message.keyboardButtons) {
return undefined;
@ -48,13 +37,13 @@ const BotKeyboardMenu: FC<OwnProps & StateProps> = ({
return (
<Menu
isOpen={isOpen || forceOpen}
isOpen={isOpen}
autoClose={isKeyboardSingleUse}
positionX="right"
positionY="bottom"
onClose={handleClose}
onClose={onClose}
className="BotKeyboardMenu"
onCloseAnimationEnd={handleClose}
onCloseAnimationEnd={onClose}
onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined}
onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined}
noCompact

View File

@ -56,6 +56,7 @@ const InvoiceMediaPreview: FC<OwnProps> = ({
openInvoice({
chatId,
messageId: id,
isExtendedMedia: true,
});
}, [chatId, id, openInvoice]);

View File

@ -10,6 +10,8 @@ import './ConfirmPayment.scss';
export type OwnProps = {
url: string;
noRedirect?: boolean;
onClose: NoneToVoidFunction;
};
interface IframeCallbackEvent {
@ -19,8 +21,8 @@ interface IframeCallbackEvent {
};
}
const ConfirmPayment: FC<OwnProps> = ({ url }) => {
const { closePaymentModal, openTelegramLink } = getActions();
const ConfirmPayment: FC<OwnProps> = ({ url, noRedirect, onClose }) => {
const { openTelegramLink } = getActions();
const lang = useLang();
@ -33,13 +35,16 @@ const ConfirmPayment: FC<OwnProps> = ({ url }) => {
return;
}
const linkUrl = TME_LINK_PREFIX + eventData.path_full;
openTelegramLink({ url: linkUrl });
closePaymentModal();
if (!noRedirect) {
const linkUrl = TME_LINK_PREFIX + eventData.path_full;
openTelegramLink({ url: linkUrl });
}
onClose();
} catch (err) {
// Ignore other messages
}
}, [closePaymentModal, openTelegramLink]);
}, [onClose, noRedirect, openTelegramLink]);
useEffect(() => {
window.addEventListener('message', handleMessage);

View File

@ -4,7 +4,7 @@ $modalHeaderAndFooterHeight: 8.375rem;
&.recurring {
.Transition {
height: 33rem;
max-height: calc(90vh - $modalHeaderAndFooterHeight);
max-height: calc(92vh - $modalHeaderAndFooterHeight);
}
}
@ -32,7 +32,7 @@ $modalHeaderAndFooterHeight: 8.375rem;
}
.Transition {
height: min(27rem, 60vh);
height: min(27rem, 68vh);
}
.empty-content {

View File

@ -39,7 +39,7 @@ const SUPPORTED_PROVIDERS = new Set([DEFAULT_PROVIDER, DONATE_PROVIDER]);
export type OwnProps = {
isOpen?: boolean;
onClose: () => void;
onClose: NoneToVoidFunction;
};
type StateProps = {
@ -64,6 +64,7 @@ type StateProps = {
stripeId?: string;
savedCredentials?: ApiPaymentCredentials[];
passwordValidUntil?: number;
isExtendedMedia?: boolean;
};
type GlobalStateProps = Pick<GlobalState['payment'], (
@ -105,6 +106,7 @@ const PaymentModal: FC<OwnProps & StateProps & GlobalStateProps> = ({
stripeId,
savedCredentials,
passwordValidUntil,
isExtendedMedia,
}) => {
const {
loadPasswordInfo,
@ -321,6 +323,8 @@ const PaymentModal: FC<OwnProps & StateProps & GlobalStateProps> = ({
return (
<ConfirmPayment
url={confirmPaymentUrl!}
noRedirect={isExtendedMedia}
onClose={closeModal}
/>
);
default:
@ -476,8 +480,27 @@ const PaymentModal: FC<OwnProps & StateProps & GlobalStateProps> = ({
? lang('Checkout.PayPrice', formatCurrency(totalPrice, currency!, lang.code))
: lang('Next');
const isSubmitDisabled = isLoading
|| Boolean(step === PaymentStep.Checkout && invoice?.isRecurring && !isTosAccepted);
function getIsSubmitDisabled() {
if (isLoading) {
return true;
}
switch (step) {
case PaymentStep.Checkout:
return Boolean(invoice?.isRecurring && !isTosAccepted);
case PaymentStep.PaymentInfo:
return Boolean(
paymentState.cardNumber === ''
|| (needCardholderName && paymentState.cardholder === '')
|| paymentState.cvv === ''
|| paymentState.expiry === '',
);
default:
return false;
}
}
if (isProviderError) {
return (
@ -501,6 +524,8 @@ const PaymentModal: FC<OwnProps & StateProps & GlobalStateProps> = ({
);
}
const isSubmitDisabled = getIsSubmitDisabled();
return (
<Modal
className={buildClassName('PaymentModal', invoice?.isRecurring && 'recurring')}
@ -569,6 +594,7 @@ export default memo(withGlobal<OwnProps>(
smartGlocalCredentials,
savedCredentials,
temporaryPassword,
isExtendedMedia,
} = global.payment;
const chat = inputInvoice && 'chatId' in inputInvoice ? selectChat(global, inputInvoice.chatId) : undefined;
@ -615,6 +641,7 @@ export default memo(withGlobal<OwnProps>(
stripeId: stripeCredentials?.id,
savedCredentials,
passwordValidUntil: temporaryPassword?.validUntil,
isExtendedMedia,
};
},
)(PaymentModal));

View File

@ -144,7 +144,7 @@
flex-grow: 1;
padding: 1rem;
overflow-y: auto;
max-height: 90vh;
max-height: 92vh;
b,
strong {

View File

@ -25,7 +25,6 @@ import {
setPaymentForm,
setStripeCardInfo,
setReceipt,
clearPayment,
closeInvoice,
setSmartGlocalCardInfo, addUsers, setInvoiceInfo, updatePayment,
} from '../../reducers';
@ -82,6 +81,7 @@ addActionHandler('openInvoice', async (global, actions, payload) => {
inputInvoice: payload,
isPaymentModalOpen: true,
status: 'cancelled',
isExtendedMedia: (payload as any).isExtendedMedia,
},
});
});
@ -217,7 +217,6 @@ addActionHandler('sendPaymentForm', async (global, actions, payload) => {
}
global = getGlobal();
global = clearPayment(global);
global = updatePayment(global, { status: 'paid' });
global = closeInvoice(global);
setGlobal(global);

View File

@ -1,7 +1,7 @@
import { addActionHandler } from '../../index';
import { IS_PRODUCTION_HOST } from '../../../util/environment';
import { clearPayment } from '../../reducers';
import { closeInvoice } from '../../reducers';
import * as langProvider from '../../../util/langProvider';
import { formatCurrency } from '../../../util/formatCurrency';
import { selectChatMessage } from '../../selectors';
@ -28,8 +28,9 @@ addActionHandler('apiUpdate', (global, actions, update) => {
// On the production host, the payment frame receives a message with the payment event,
// after which the payment form closes. In other cases, the payment form must be closed manually.
// Closing the invoice will cause the closing of the Payment Modal dialog and then closing the payment.
if (!IS_PRODUCTION_HOST) {
global = clearPayment(global);
global = closeInvoice(global);
}
if (update.slug && inputInvoice && 'slug' in inputInvoice && inputInvoice.slug !== update.slug) {

View File

@ -110,5 +110,5 @@ export function clearPayment(global: GlobalState): GlobalState {
}
export function closeInvoice(global: GlobalState): GlobalState {
return updatePayment(global, { isPaymentModalOpen: undefined });
return updatePayment(global, { isPaymentModalOpen: undefined, isExtendedMedia: undefined });
}

View File

@ -504,6 +504,7 @@ export type GlobalState = {
description?: string;
};
isPaymentModalOpen?: boolean;
isExtendedMedia?: boolean;
confirmPaymentUrl?: string;
temporaryPassword?: {
value: string;