diff --git a/src/components/popups/payment.ts b/src/components/popups/payment.ts index 03b89d05..d341b8a3 100644 --- a/src/components/popups/payment.ts +++ b/src/components/popups/payment.ts @@ -27,9 +27,11 @@ import { AccountTmpPassword, InputInvoice, InputPaymentCredentials, LabeledPrice import I18n, { i18n, LangPackKey, _i18n } from "../../lib/langPack"; import { ApiError } from "../../lib/mtproto/apiManager"; import wrapEmojiText from "../../lib/richTextProcessor/wrapEmojiText"; +import wrapRichText from "../../lib/richTextProcessor/wrapRichText"; import rootScope from "../../lib/rootScope"; import AvatarElement from "../avatar"; import Button from "../button"; +import CheckboxField from "../checkboxField"; import PeerTitle from "../peerTitle"; import { putPreloader } from "../putPreloader"; import Row from "../row"; @@ -240,10 +242,6 @@ export default class PopupPayment extends PopupElement { // console.log(paymentForm, lastRequestedInfo); - await peerTitle.update({peerId: paymentForm.bot_id.toPeerId()}); - preloaderContainer.remove(); - this.element.classList.remove('is-loading'); - const wrapAmount = (amount: string | number, skipSymbol?: boolean) => { return paymentsWrapCurrencyAmount(amount, currency, skipSymbol); }; @@ -251,6 +249,13 @@ export default class PopupPayment extends PopupElement { const {invoice} = paymentForm; const currency = invoice.currency; + const isRecurring = invoice.pFlags.recurring && !isReceipt; + + await peerTitle.update({peerId: paymentForm.bot_id.toPeerId()}); + const peerTitle2 = isRecurring ? await wrapPeerTitle({peerId: paymentForm.bot_id.toPeerId()}) : undefined; + preloaderContainer.remove(); + this.element.classList.remove('is-loading'); + const makeLabel = () => { const labelEl = document.createElement('div'); labelEl.classList.add(pricesClassName + '-price'); @@ -669,9 +674,23 @@ export default class PopupPayment extends PopupElement { shippingEmailRow, shippingPhoneRow, ].filter(Boolean); + + const acceptTermsCheckboxField = isRecurring && new CheckboxField({ + text: 'Payments.Recurrent.Accept', + textArgs: [wrapRichText(invoice.recurring_terms_url), peerTitle2] + }); + + const acceptTermsRow = isRecurring && createRow({ + checkboxField: acceptTermsCheckboxField, + noCheckboxSubtitle: true + }); + + const recurringElements = isRecurring ? [document.createElement('hr'), acceptTermsRow.container] : []; + this.scrollable.append(...[ document.createElement('hr'), - ...rows.map((row) => row.container) + ...rows.map((row) => row.container), + ...recurringElements ].filter(Boolean)); /// @@ -779,7 +798,11 @@ export default class PopupPayment extends PopupElement { }); }; - let payButton: HTMLElement; + const onChange = () => { + payButton.disabled = !!(acceptTermsCheckboxField && !acceptTermsCheckboxField.checked); + }; + + let payButton: HTMLButtonElement; if(isReceipt) { payButton = PaymentButton({ onClick: () => this.hide(), @@ -792,6 +815,11 @@ export default class PopupPayment extends PopupElement { }); } + onChange(); + if(acceptTermsCheckboxField) { + acceptTermsCheckboxField.input.addEventListener('change', onChange); + } + this.body.append(this.btnConfirmOnEnter = payButton); this.onContentUpdate(); diff --git a/src/components/wrappers/messageActionTextNewUnsafe.ts b/src/components/wrappers/messageActionTextNewUnsafe.ts index 71f302a1..342594ca 100644 --- a/src/components/wrappers/messageActionTextNewUnsafe.ts +++ b/src/components/wrappers/messageActionTextNewUnsafe.ts @@ -250,7 +250,9 @@ export default async function wrapMessageActionTextNewUnsafe(message: MyMessage, } case 'messageActionPaymentSent': { - langPackKey = 'PaymentSuccessfullyPaidNoItem'; + const isRecurringInit = action.pFlags.recurring_init; + const isRecurringUsed = action.pFlags.recurring_used; + langPackKey = isRecurringUsed ? 'Chat.Service.PaymentSentRecurringUsedNoTitle' : (isRecurringInit ? 'Chat.Service.PaymentSentRecurringInitNoTitle' : 'Chat.Service.PaymentSent1NoTitle'); const price = paymentsWrapCurrencyAmount(action.total_amount, action.currency); args = [price, getNameDivHTML(message.peerId, plain)]; @@ -263,7 +265,7 @@ export default async function wrapMessageActionTextNewUnsafe(message: MyMessage, if(!invoiceMessage) { managers.appMessagesManager.fetchMessageReplyTo(message); } else { - langPackKey = 'PaymentSuccessfullyPaid'; + langPackKey = isRecurringUsed ? 'Chat.Service.PaymentSentRecurringUsed' : (isRecurringInit ? 'Chat.Service.PaymentSentRecurringInit' : 'Chat.Service.PaymentSent1'); args.push(wrapLinkToMessage(invoiceMessage, plain)); } } diff --git a/src/lang.ts b/src/lang.ts index 3aeacdfb..d9f598b5 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -703,10 +703,6 @@ const lang = { "PaymentInvoice": "INVOICE", "PaymentTestInvoice": "TEST INVOICE", "PaymentReceipt": "Receipt", - "PaymentSuccessfullyPaid": "You successfully transferred %1$s to %2$s for %3$s", - "PaymentSuccessfullyPaidNoItem": "You successfully transferred %1$s to %2$s", - // "PaymentSuccessfullyPaidRecurrent": "You successfully transferred %1$s to %2$s for %3$s and allowed future recurring payments", - // "PaymentSuccessfullyPaidNoItemRecurrent": "You successfully transferred %1$s to %2$s and allowed future recurring payments", "PaymentCheckout": "Checkout", "PaymentTransactionTotal": "Total", "PaymentTip": "Tip", @@ -835,6 +831,12 @@ const lang = { "Chat.Service.Channel.UpdatedVideo": "Channel video updated", "Chat.Service.BotPermissionAllowed": "You allowed this bot to message you when you logged in on %@", "Chat.Service.Group.UpdatedPinnedMessage": "%@ pinned \"%@\"", + "Chat.Service.PaymentSent1": "You have successfully transferred **%1$@** to **%2$@** for **%3$@**", + "Chat.Service.PaymentSent1NoTitle": "You have successfully transferred **%1$@** to **%2$@**", + "Chat.Service.PaymentSentRecurringInit": "You successfully transferred **%1$@** to **%2$@** for **%3$@** and allowed future recurring payments", + "Chat.Service.PaymentSentRecurringInitNoTitle": "You successfully transferred **%1$@** to **%2$@** and allowed future recurring payments", + "Chat.Service.PaymentSentRecurringUsed": "You have just successfully transferred **%1$@** to **%2$@** for **%3$@** via recurrent payments", + "Chat.Service.PaymentSentRecurringUsedNoTitle": "You have just successfully transferred **%1$@** to **%2$@** via recurrent payments", "Chat.Service.VoiceChatStarted": "%1$@ started a [video chat](open)", "Chat.Service.VoiceChatStartedYou": "You started a [video chat](open)", "Chat.Service.VoiceChatStarted.Channel": "[Live Stream](open) started", @@ -985,6 +987,7 @@ const lang = { "NewPoll.Quiz": "Quiz Mode", "Notification.Contact.Reacted": "%1$@ to your \"%2$@\"", // "Notification.Group.Reacted": "%1$@: %2$@ to your \"%3$@\"", + "Payments.Recurrent.Accept": "I accept [Terms of Service]() of **%@**.", "Peer.Activity.User.PlayingGame": "playing a game", "Peer.Activity.User.TypingText": "typing", "Peer.Activity.User.SendingPhoto": "sending a photo", diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index c0957828..070b8bdc 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -80,6 +80,7 @@ import noop from "../../helpers/noop"; import DialogsPlaceholder from "../../helpers/dialogsPlaceholder"; import pause from "../../helpers/schedulers/pause"; import apiManagerProxy from "../mtproto/mtprotoworker"; +import filterAsync from "../../helpers/array/filterAsync"; export const DIALOG_LIST_ELEMENT_TAG = 'A'; @@ -1287,10 +1288,12 @@ export class AppDialogsManager { this.loadContacts = () => { const pageCount = windowSize.height / 60 | 0; - const arr = contacts.splice(0, pageCount).filter(this.verifyPeerIdForContacts); + const promise = filterAsync(contacts.splice(0, pageCount), this.verifyPeerIdForContacts); - arr.forEach((peerId) => { - sortedUserList.add(peerId); + promise.then((arr) => { + arr.forEach((peerId) => { + sortedUserList.add(peerId); + }); }); if(!contacts.length) { @@ -1300,12 +1303,12 @@ export class AppDialogsManager { this.loadContacts(); - this.processContact = (peerId) => { + this.processContact = async(peerId) => { if(peerId.isAnyChat()) { return; } - const good = this.verifyPeerIdForContacts(peerId); + const good = await this.verifyPeerIdForContacts(peerId); const added = sortedUserList.has(peerId); if(!added && good) sortedUserList.add(peerId); else if(added && !good) sortedUserList.delete(peerId); diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 627db2ec..12acb485 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -4027,7 +4027,7 @@ export class AppMessagesManager extends AppManager { this.onUpdateNewMessage(update); } - if(message._ === 'messageService' && message.action._ === 'messageActionPaymentSent') { + if(message._ === 'messageService' && message.action._ === 'messageActionPaymentSent' && message.reply_to) { this.rootScope.dispatchEvent('payment_sent', { peerId: message.reply_to.reply_to_peer_id ? getPeerId(message.reply_to.reply_to_peer_id) : message.peerId, mid: message.reply_to_mid diff --git a/src/lib/langPack.ts b/src/lib/langPack.ts index 549789d1..471fa8c4 100644 --- a/src/lib/langPack.ts +++ b/src/lib/langPack.ts @@ -368,6 +368,11 @@ namespace I18n { a.target = '_blank'; } else { a = args[indexHolder.i++] as HTMLAnchorElement; + + if(a instanceof DocumentFragment) { // right after wrapRichText + a = a.firstChild as any; + } + a.textContent = ''; // reset content } diff --git a/src/lib/mtproto/dcConfigurator.ts b/src/lib/mtproto/dcConfigurator.ts index 68dfb581..63cde200 100644 --- a/src/lib/mtproto/dcConfigurator.ts +++ b/src/lib/mtproto/dcConfigurator.ts @@ -65,7 +65,7 @@ export class DcConfigurator { /// #if MTPROTO_HAS_WS private transportSocket = (dcId: DcId, connectionType: ConnectionType, suffix: string, premium?: boolean) => { - const path = connectionType !== 'client' ? 'apiws' + (premium ? PREMIUM_SUFFIX : '') : ('apiws' + TEST_SUFFIX); + const path = connectionType !== 'client' ? 'apiws' + (premium ? PREMIUM_SUFFIX : TEST_SUFFIX) : ('apiws' + TEST_SUFFIX); const chosenServer = `wss://${App.suffix.toLowerCase()}ws${dcId}${suffix}.web.telegram.org/${path}`; const logSuffix = connectionType === 'upload' ? '-U' : connectionType === 'download' ? '-D' : ''; diff --git a/src/scss/partials/_checkbox.scss b/src/scss/partials/_checkbox.scss index 58959884..90a3c692 100644 --- a/src/scss/partials/_checkbox.scss +++ b/src/scss/partials/_checkbox.scss @@ -124,6 +124,10 @@ left: auto; } } + + .anchor-url { + pointer-events: all; + } } .checkbox-ripple { diff --git a/src/scss/partials/_row.scss b/src/scss/partials/_row.scss index 6614c594..6bb6ba11 100644 --- a/src/scss/partials/_row.scss +++ b/src/scss/partials/_row.scss @@ -119,7 +119,9 @@ $row-border-radius: $border-radius-medium; } .checkbox-field { + margin-top: 0; margin-right: 0; + margin-bottom: 0; height: auto; .checkbox-caption { diff --git a/src/scss/partials/popups/_payment.scss b/src/scss/partials/popups/_payment.scss index 38a45463..78425ccf 100644 --- a/src/scss/partials/popups/_payment.scss +++ b/src/scss/partials/popups/_payment.scss @@ -36,6 +36,8 @@ hr { display: block !important; + margin: .5rem 0 !important; + padding: 0 !important; } .input-field { @@ -158,7 +160,7 @@ &-prices { display: flex; flex-direction: column; - margin: 1rem .25rem; + margin: 1rem .25rem .5rem; &-price { color: var(--secondary-text-color);