diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 91385fca1..52d474981 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/notifications_manager.h" #include "window/window_session_controller.h" #include "storage/storage_shared_media.h" +#include "payments/payments_checkout_process.h" // CheckoutProcess::Start. #include "ui/text/format_values.h" #include "ui/text/text_options.h" @@ -522,18 +523,28 @@ bool HistoryService::updateDependent(bool force) { } if (!dependent->lnk) { - dependent->lnk = goToMessageClickHandler(history()->peer, dependent->msgId); + dependent->lnk = goToMessageClickHandler( + (dependent->peerId + ? history()->owner().peer(dependent->peerId) + : history()->peer), + dependent->msgId); } auto gotDependencyItem = false; if (!dependent->msg) { - dependent->msg = history()->owner().message(channelId(), dependent->msgId); + dependent->msg = history()->owner().message( + (dependent->peerId + ? peerToChannel(dependent->peerId) + : channelId()), + dependent->msgId); if (dependent->msg) { if (dependent->msg->isEmpty()) { // Really it is deleted. dependent->msg = nullptr; force = true; } else { - history()->owner().registerDependentMessage(this, dependent->msg); + history()->owner().registerDependentMessage( + this, + dependent->msg); gotDependencyItem = true; } } @@ -749,14 +760,14 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() { auto payment = Get(); auto invoiceTitle = [&] { - if (payment && payment->msg) { + if (payment->msg) { if (const auto media = payment->msg->media()) { if (const auto invoice = media->invoice()) { - return invoice->title; + return textcmdLink(1, invoice->title); } } return tr::lng_deleted_message(tr::now); - } else if (payment && payment->msgId) { + } else if (payment->msgId) { return tr::lng_contacts_loading(tr::now); } return QString(); @@ -766,6 +777,9 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() { result.text = tr::lng_action_payment_done(tr::now, lt_amount, payment->amount, lt_user, history()->peer->name); } else { result.text = tr::lng_action_payment_done_for(tr::now, lt_amount, payment->amount, lt_user, history()->peer->name, lt_invoice, invoiceTitle); + if (payment && payment->msg) { + result.links.push_back(payment->lnk); + } } return result; } @@ -958,9 +972,16 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) { UpdateComponents(HistoryServicePayment::Bit()); const auto amount = data.vtotal_amount().v; const auto currency = qs(data.vcurrency()); - Get()->amount = Ui::FillAmountAndCurrency( - amount, - currency); + const auto payment = Get(); + const auto id = fullId(); + const auto owner = &history()->owner(); + payment->amount = Ui::FillAmountAndCurrency(amount, currency); + payment->invoiceLink = std::make_shared([=] { + using namespace Payments; + if (const auto item = owner->message(id)) { + CheckoutProcess::Start(item, Mode::Receipt); + } + }); } else if (message.vaction().type() == mtpc_messageActionGroupCall) { const auto &data = message.vaction().c_messageActionGroupCall(); if (data.vduration()) { @@ -1020,21 +1041,22 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) { } if (const auto replyTo = message.vreply_to()) { replyTo->match([&](const MTPDmessageReplyHeader &data) { - const auto peer = data.vreply_to_peer_id() + const auto peerId = data.vreply_to_peer_id() ? peerFromMTP(*data.vreply_to_peer_id()) : history()->peer->id; - if (!peer || peer == history()->peer->id) { - if (message.vaction().type() == mtpc_messageActionPinMessage) { - UpdateComponents(HistoryServicePinned::Bit()); - } - if (const auto dependent = GetDependentData()) { - dependent->msgId = data.vreply_to_msg_id().v; - if (!updateDependent()) { - history()->session().api().requestMessageData( - history()->peer->asChannel(), - dependent->msgId, - HistoryDependentItemCallback(this)); - } + if (message.vaction().type() == mtpc_messageActionPinMessage) { + UpdateComponents(HistoryServicePinned::Bit()); + } + if (const auto dependent = GetDependentData()) { + dependent->peerId = (peerId != history()->peer->id) + ? peerId + : 0; + dependent->msgId = data.vreply_to_msg_id().v; + if (!updateDependent()) { + history()->session().api().requestMessageData( + history()->peer->asChannel(), + dependent->msgId, + HistoryDependentItemCallback(this)); } } }); diff --git a/Telegram/SourceFiles/history/history_service.h b/Telegram/SourceFiles/history/history_service.h index 7b7bcbab3..1f7c4854d 100644 --- a/Telegram/SourceFiles/history/history_service.h +++ b/Telegram/SourceFiles/history/history_service.h @@ -14,6 +14,7 @@ class Service; } // namespace HistoryView struct HistoryServiceDependentData { + PeerId peerId = 0; MsgId msgId = 0; HistoryItem *msg = nullptr; ClickHandlerPtr lnk; @@ -34,6 +35,7 @@ struct HistoryServicePayment : public RuntimeComponent , public HistoryServiceDependentData { QString amount; + ClickHandlerPtr invoiceLink; }; struct HistoryServiceSelfDestruct diff --git a/Telegram/SourceFiles/history/view/history_view_service_message.cpp b/Telegram/SourceFiles/history/view/history_view_service_message.cpp index b091c8573..693808180 100644 --- a/Telegram/SourceFiles/history/view/history_view_service_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_service_message.cpp @@ -523,7 +523,7 @@ TextState Service::textState(QPoint point, StateRequest request) const { if (!result.link && result.cursor == CursorState::Text && g.contains(point)) { - result.link = payment->lnk; + result.link = payment->invoiceLink; } } } else if (media) { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index a88bb5dfe..ca2af7d37 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -696,24 +696,24 @@ void Video::initDimensions() { const auto withThumb = withThumbnail(); _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; - int32 textWidth = _maxw - (withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0); - TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; + const auto textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip); + TextParseOptions titleOpts = { 0, textWidth, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; auto title = TextUtilities::SingleLine(_result->getLayoutTitle()); if (title.isEmpty()) { title = tr::lng_media_video(tr::now); } _title.setText(st::semiboldTextStyle, title, titleOpts); - int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height); + int32 titleHeight = qMin(_title.countHeight(textWidth), 2 * st::semiboldFont->height); int32 descriptionLines = withThumb ? (titleHeight > st::semiboldFont->height ? 1 : 2) : 3; - TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, descriptionLines * st::normalFont->height, Qt::LayoutDirectionAuto }; + TextParseOptions descriptionOpts = { TextParseMultiline, textWidth, descriptionLines * st::normalFont->height, Qt::LayoutDirectionAuto }; QString description = _result->getLayoutDescription(); if (description.isEmpty()) { description = _duration; } _description.setText(st::defaultTextStyle, description, descriptionOpts); - int32 descriptionHeight = qMin(_description.countHeight(_maxw), descriptionLines * st::normalFont->height); + int32 descriptionHeight = qMin(_description.countHeight(textWidth), descriptionLines * st::normalFont->height); _minh = st::inlineThumbSize; _minh += st::inlineRowMargin * 2 + st::inlineRowBorder; @@ -1073,13 +1073,13 @@ Contact::Contact(not_null context, not_null result) void Contact::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip); - TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto }; + TextParseOptions titleOpts = { 0, textWidth, st::semiboldFont->height, Qt::LayoutDirectionAuto }; _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts); - int32 titleHeight = qMin(_title.countHeight(_maxw), st::semiboldFont->height); + int32 titleHeight = qMin(_title.countHeight(textWidth), st::semiboldFont->height); - TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto }; + TextParseOptions descriptionOpts = { TextParseMultiline, textWidth, st::normalFont->height, Qt::LayoutDirectionAuto }; _description.setText(st::defaultTextStyle, _result->getLayoutDescription(), descriptionOpts); - int32 descriptionHeight = qMin(_description.countHeight(_maxw), st::normalFont->height); + int32 descriptionHeight = qMin(_description.countHeight(textWidth), st::normalFont->height); _minh = st::inlineFileSize; _minh += st::inlineRowMargin * 2 + st::inlineRowBorder; @@ -1170,7 +1170,7 @@ Article::Article( void Article::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; - int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0); + int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::emojiPanHeaderLeft - st::inlineResultsLeft)); TextParseOptions titleOpts = { 0, textWidth, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts); int32 titleHeight = qMin(_title.countHeight(textWidth), 2 * st::semiboldFont->height); @@ -1192,8 +1192,9 @@ int32 Article::resizeGetHeight(int32 width) { if (_url) { _urlText = getResultUrl(); _urlWidth = st::normalFont->width(_urlText); - if (_urlWidth > _width - st::inlineThumbSize - st::inlineThumbSkip) { - _urlText = st::normalFont->elided(_urlText, _width - st::inlineThumbSize - st::inlineThumbSkip); + int32 textWidth = _width - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::emojiPanHeaderLeft - st::inlineResultsLeft)); + if (_urlWidth > textWidth) { + _urlText = st::normalFont->elided(_urlText, textWidth); _urlWidth = st::normalFont->width(_urlText); } } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index 22bcb7511..a605a7122 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -51,7 +51,7 @@ Result::Result(not_null session, const Creator &creator) std::unique_ptr Result::Create( not_null session, uint64 queryId, - const MTPBotInlineResult &mtpData) { + const MTPBotInlineResult &data) { using Type = Result::Type; const auto type = [&] { @@ -69,7 +69,7 @@ std::unique_ptr Result::Create( { u"geo"_q, Type::Geo }, { u"game"_q, Type::Game }, }; - const auto type = mtpData.match([](const auto &data) { + const auto type = data.match([](const auto &data) { return qs(data.vtype()); }); const auto i = kStringToTypeMap.find(type); @@ -82,16 +82,13 @@ std::unique_ptr Result::Create( auto result = std::make_unique( session, Creator{ queryId, type }); - const MTPBotInlineMessage *message = nullptr; - switch (mtpData.type()) { - case mtpc_botInlineResult: { - const auto &r = mtpData.c_botInlineResult(); - result->_id = qs(r.vid()); - result->_title = qs(r.vtitle().value_or_empty()); - result->_description = qs(r.vdescription().value_or_empty()); - result->_url = qs(r.vurl().value_or_empty()); + const auto message = data.match([&](const MTPDbotInlineResult &data) { + result->_id = qs(data.vid()); + result->_title = qs(data.vtitle().value_or_empty()); + result->_description = qs(data.vdescription().value_or_empty()); + result->_url = qs(data.vurl().value_or_empty()); const auto thumbMime = [&] { - if (const auto thumb = r.vthumb()) { + if (const auto thumb = data.vthumb()) { return thumb->match([&](const auto &data) { return data.vmime_type().v; }); @@ -99,7 +96,7 @@ std::unique_ptr Result::Create( return QByteArray(); }(); const auto contentMime = [&] { - if (const auto content = r.vcontent()) { + if (const auto content = data.vcontent()) { return content->match([&](const auto &data) { return data.vmime_type().v; }); @@ -109,49 +106,45 @@ std::unique_ptr Result::Create( const auto imageThumb = !thumbMime.isEmpty() && (thumbMime != kVideoThumbMime); const auto videoThumb = !thumbMime.isEmpty() && !imageThumb; - if (const auto content = r.vcontent()) { + if (const auto content = data.vcontent()) { result->_content_url = GetContentUrl(*content); if (result->_type == Type::Photo) { result->_photo = session->data().photoFromWeb( *content, (imageThumb - ? Images::FromWebDocument(*r.vthumb()) + ? Images::FromWebDocument(*data.vthumb()) : ImageLocation())); } else if (contentMime != "text/html"_q) { result->_document = session->data().documentFromWeb( result->adjustAttributes(*content), (imageThumb - ? Images::FromWebDocument(*r.vthumb()) + ? Images::FromWebDocument(*data.vthumb()) : ImageLocation()), (videoThumb - ? Images::FromWebDocument(*r.vthumb()) + ? Images::FromWebDocument(*data.vthumb()) : ImageLocation())); } } if (!result->_photo && !result->_document && imageThumb) { result->_thumbnail.update(result->_session, ImageWithLocation{ - .location = Images::FromWebDocument(*r.vthumb()) - }); + .location = Images::FromWebDocument(*data.vthumb()) + }); } - message = &r.vsend_message(); - } break; - case mtpc_botInlineMediaResult: { - const auto &r = mtpData.c_botInlineMediaResult(); - result->_id = qs(r.vid()); - result->_title = qs(r.vtitle().value_or_empty()); - result->_description = qs(r.vdescription().value_or_empty()); - if (const auto photo = r.vphoto()) { + return &data.vsend_message(); + }, [&](const MTPDbotInlineMediaResult &data) { + result->_id = qs(data.vid()); + result->_title = qs(data.vtitle().value_or_empty()); + result->_description = qs(data.vdescription().value_or_empty()); + if (const auto photo = data.vphoto()) { result->_photo = session->data().processPhoto(*photo); } - if (const auto document = r.vdocument()) { + if (const auto document = data.vdocument()) { result->_document = session->data().processDocument(*document); } - message = &r.vsend_message(); - } break; - } + return &data.vsend_message(); + }); if ((result->_photo && result->_photo->isNull()) - || (result->_document && result->_document->isNull()) - || !message) { + || (result->_document && result->_document->isNull())) { return nullptr; } @@ -248,7 +241,23 @@ std::unique_ptr Result::Create( qs(data.vlast_name()), qs(data.vphone_number())); }, [&](const MTPDbotInlineMessageMediaInvoice &data) { - // #TODO payments + using Flag = MTPDmessageMediaInvoice::Flag; + const auto media = MTP_messageMediaInvoice( + MTP_flags((data.is_shipping_address_requested() + ? Flag::f_shipping_address_requested + : Flag(0)) + | (data.is_test() ? Flag::f_test : Flag(0)) + | (data.vphoto() ? Flag::f_photo : Flag(0))), + data.vtitle(), + data.vdescription(), + data.vphoto() ? (*data.vphoto()) : MTPWebDocument(), + MTPint(), // receipt_msg_id + data.vcurrency(), + data.vtotal_amount(), + MTP_string(QString())); // start_param + result->sendData = std::make_unique( + session, + media); }); if (!result->sendData || !result->sendData->isValid()) { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp index 6b3cae70e..07893c3e7 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp @@ -264,5 +264,15 @@ QString SendGame::getErrorOnSend( return error.value_or(QString()); } +auto SendInvoice::getSentMessageFields() const -> SentMTPMessageFields { + SentMTPMessageFields result; + result.media = _media; + return result; +} + +QString SendInvoice::getLayoutDescription(const Result *owner) const { + return qs(_media.c_messageMediaInvoice().vdescription()); +} + } // namespace internal } // namespace InlineBots diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h index c977ebd1d..012d80554 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h @@ -351,5 +351,27 @@ private: }; +class SendInvoice : public SendDataCommon { +public: + SendInvoice( + not_null session, + MTPMessageMedia media) + : SendDataCommon(session) + , _media(media) { + } + + bool isValid() const override { + return true; + } + + SentMTPMessageFields getSentMessageFields() const override; + + QString getLayoutDescription(const Result *owner) const override; + +private: + MTPMessageMedia _media; + +}; + } // namespace internal } // namespace InlineBots diff --git a/Telegram/SourceFiles/payments/payments_checkout_process.cpp b/Telegram/SourceFiles/payments/payments_checkout_process.cpp index 42f287afb..aa4565a35 100644 --- a/Telegram/SourceFiles/payments/payments_checkout_process.cpp +++ b/Telegram/SourceFiles/payments/payments_checkout_process.cpp @@ -174,7 +174,8 @@ void CheckoutProcess::handleError(const Error &error) { } break; case Error::Type::Validate: { - if (_submitState == SubmitState::Validation) { + if (_submitState == SubmitState::Validation + || _submitState == SubmitState::Validated) { _submitState = SubmitState::None; } if (_initialSilentValidation) { @@ -281,7 +282,9 @@ void CheckoutProcess::panelCloseSure() { } void CheckoutProcess::panelSubmit() { - if (_submitState == SubmitState::Validation + if (_form->invoice().receipt.paid) { + panelCloseSure(); + } else if (_submitState == SubmitState::Validation || _submitState == SubmitState::Finishing) { return; } diff --git a/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp b/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp index 95d647ec9..32090c577 100644 --- a/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp @@ -43,11 +43,15 @@ FormSummary::FormSummary( , _topShadow(this) , _bottomShadow(this) , _submit( - this, - tr::lng_payments_pay_amount( + this, + (_invoice.receipt.paid + ? tr::lng_about_done() + : tr::lng_payments_pay_amount( lt_amount, - rpl::single(formatAmount(computeTotalAmount()))), - st::paymentsPanelSubmit) { + rpl::single(formatAmount(computeTotalAmount())))), + (_invoice.receipt.paid + ? st::passportPanelSaveValue + : st::paymentsPanelSubmit)) { setupControls(); } @@ -220,13 +224,13 @@ void FormSummary::setupPrices(not_null layout) { Settings::AddSkip(layout, st::paymentsPricesTopSkip); if (_invoice.receipt) { - Settings::AddDivider(layout); - Settings::AddSkip(layout, st::paymentsPricesBottomSkip); addRow( tr::lng_payments_date_label(tr::now), langDateTime(base::unixtime::parse(_invoice.receipt.date)), true); Settings::AddSkip(layout, st::paymentsPricesBottomSkip); + Settings::AddDivider(layout); + Settings::AddSkip(layout, st::paymentsPricesBottomSkip); } const auto add = [&](