From d10df1a7050c101a47857f50b6e772cf088d3a6a Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Thu, 10 Nov 2022 18:28:05 +0400 Subject: [PATCH] Payment Modal: Various fixes (#2124) --- src/api/types/messages.ts | 1 + .../common/helpers/detectCardType.ts | 6 ++-- .../middle/composer/BotKeyboardMenu.tsx | 19 +++-------- .../middle/message/InvoiceMediaPreview.tsx | 1 + src/components/payment/ConfirmPayment.tsx | 17 ++++++---- src/components/payment/PaymentModal.scss | 4 +-- src/components/payment/PaymentModal.tsx | 33 +++++++++++++++++-- src/components/ui/Modal.scss | 2 +- src/global/actions/api/payments.ts | 3 +- src/global/actions/apiUpdaters/payments.ts | 5 +-- src/global/reducers/payments.ts | 2 +- src/global/types.ts | 1 + 12 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/api/types/messages.ts b/src/api/types/messages.ts index f710133b5..a02104793 100644 --- a/src/api/types/messages.ts +++ b/src/api/types/messages.ts @@ -166,6 +166,7 @@ export interface ApiPoll { export type ApiInputInvoice = { chatId: string; messageId: number; + isExtendedMedia?: boolean; } | { slug: string; }; diff --git a/src/components/common/helpers/detectCardType.ts b/src/components/common/helpers/detectCardType.ts index 34f6df6ef..2fa466b4a 100644 --- a/src/components/common/helpers/detectCardType.ts +++ b/src/components/common/helpers/detectCardType.ts @@ -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 { diff --git a/src/components/middle/composer/BotKeyboardMenu.tsx b/src/components/middle/composer/BotKeyboardMenu.tsx index 6d540ac83..2124c7fda 100644 --- a/src/components/middle/composer/BotKeyboardMenu.tsx +++ b/src/components/middle/composer/BotKeyboardMenu.tsx @@ -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 = ({ 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 = ({ return ( = ({ openInvoice({ chatId, messageId: id, + isExtendedMedia: true, }); }, [chatId, id, openInvoice]); diff --git a/src/components/payment/ConfirmPayment.tsx b/src/components/payment/ConfirmPayment.tsx index 62b8f01f3..0477029e4 100644 --- a/src/components/payment/ConfirmPayment.tsx +++ b/src/components/payment/ConfirmPayment.tsx @@ -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 = ({ url }) => { - const { closePaymentModal, openTelegramLink } = getActions(); +const ConfirmPayment: FC = ({ url, noRedirect, onClose }) => { + const { openTelegramLink } = getActions(); const lang = useLang(); @@ -33,13 +35,16 @@ const ConfirmPayment: FC = ({ 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); diff --git a/src/components/payment/PaymentModal.scss b/src/components/payment/PaymentModal.scss index 37666be46..24ba4baf8 100644 --- a/src/components/payment/PaymentModal.scss +++ b/src/components/payment/PaymentModal.scss @@ -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 { diff --git a/src/components/payment/PaymentModal.tsx b/src/components/payment/PaymentModal.tsx index de419a5fc..ed95037f5 100644 --- a/src/components/payment/PaymentModal.tsx +++ b/src/components/payment/PaymentModal.tsx @@ -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 = ({ stripeId, savedCredentials, passwordValidUntil, + isExtendedMedia, }) => { const { loadPasswordInfo, @@ -321,6 +323,8 @@ const PaymentModal: FC = ({ return ( ); default: @@ -476,8 +480,27 @@ const PaymentModal: FC = ({ ? 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 = ({ ); } + const isSubmitDisabled = getIsSubmitDisabled(); + return ( ( 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( stripeId: stripeCredentials?.id, savedCredentials, passwordValidUntil: temporaryPassword?.validUntil, + isExtendedMedia, }; }, )(PaymentModal)); diff --git a/src/components/ui/Modal.scss b/src/components/ui/Modal.scss index 24cf1a86c..934a221bd 100644 --- a/src/components/ui/Modal.scss +++ b/src/components/ui/Modal.scss @@ -144,7 +144,7 @@ flex-grow: 1; padding: 1rem; overflow-y: auto; - max-height: 90vh; + max-height: 92vh; b, strong { diff --git a/src/global/actions/api/payments.ts b/src/global/actions/api/payments.ts index bc50edc68..85c156952 100644 --- a/src/global/actions/api/payments.ts +++ b/src/global/actions/api/payments.ts @@ -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); diff --git a/src/global/actions/apiUpdaters/payments.ts b/src/global/actions/apiUpdaters/payments.ts index 76d563112..ace516d36 100644 --- a/src/global/actions/apiUpdaters/payments.ts +++ b/src/global/actions/apiUpdaters/payments.ts @@ -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) { diff --git a/src/global/reducers/payments.ts b/src/global/reducers/payments.ts index bf0c54515..018c99149 100644 --- a/src/global/reducers/payments.ts +++ b/src/global/reducers/payments.ts @@ -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 }); } diff --git a/src/global/types.ts b/src/global/types.ts index 1af307d86..a4b7cb795 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -504,6 +504,7 @@ export type GlobalState = { description?: string; }; isPaymentModalOpen?: boolean; + isExtendedMedia?: boolean; confirmPaymentUrl?: string; temporaryPassword?: { value: string;