diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 1c5c1ebcc..fb2bf04e5 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -38,29 +38,39 @@ namespace App { if (MainWidget *m = main()) m->sendBotCommand(peer, cmd, replyTo); } - void sendBotCallback(PeerData *peer, const QByteArray &data, MsgId replyTo) { - if (MainWidget *m = main()) m->sendBotCallback(peer, data, replyTo); - } - bool insertBotCommand(const QString &cmd, bool specialGif) { if (MainWidget *m = main()) return m->insertBotCommand(cmd, specialGif); return false; } - void activateBotCommand(PeerData *peer, const HistoryMessageReplyMarkup::Button &button, MsgId replyTo) { - switch (button.type) { + void activateBotCommand(const HistoryItem *msg, int row, int col) { + const HistoryMessageReplyMarkup::Button *button = nullptr; + if (auto *markup = msg->Get()) { + if (row < markup->rows.size()) { + const HistoryMessageReplyMarkup::ButtonRow &buttonRow(markup->rows.at(row)); + if (col < buttonRow.size()) { + button = &buttonRow.at(col); + } + } + } + if (!button) return; + + switch (button->type) { case HistoryMessageReplyMarkup::Button::Default: { - // copy string before passing it to the sending method - // the original button can be destroyed inside - sendBotCommand(peer, QString(button.text), replyTo); + // Copy string before passing it to the sending method + // because the original button can be destroyed inside. + MsgId replyTo = (msg->id > 0) ? msg->id : 0; + sendBotCommand(msg->history()->peer, QString(button->text), replyTo); } break; case HistoryMessageReplyMarkup::Button::Callback: { - sendBotCallback(peer, button.data, replyTo); + if (MainWidget *m = main()) { + m->app_sendBotCallback(button, msg, row, col); + } } break; case HistoryMessageReplyMarkup::Button::Url: { - auto url = QString::fromUtf8(button.data); + auto url = QString::fromUtf8(button->data); HiddenUrlClickHandler(url).onClick(Qt::LeftButton); } break; @@ -69,7 +79,7 @@ namespace App { } break; case HistoryMessageReplyMarkup::Button::RequestPhone: { - SharePhoneConfirmBox *box = new SharePhoneConfirmBox(peer); + SharePhoneConfirmBox *box = new SharePhoneConfirmBox(msg->history()->peer); box->connect(box, SIGNAL(confirmed(PeerData*)), App::main(), SLOT(onSharePhoneWithBot(PeerData*))); Ui::showLayer(box); } break; diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 6d0651a4a..8adec8d7f 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -25,9 +25,8 @@ class LayeredWidget; namespace App { void sendBotCommand(PeerData *peer, const QString &cmd, MsgId replyTo = 0); - void sendBotCallback(PeerData *peer, const QByteArray &data, MsgId replyTo); bool insertBotCommand(const QString &cmd, bool specialGif = false); - void activateBotCommand(PeerData *peer, const HistoryMessageReplyMarkup::Button &button, MsgId replyTo = 0); + void activateBotCommand(const HistoryItem *msg, int row, int col); void searchByHashtag(const QString &tag, PeerData *inPeer); void openPeerByName(const QString &username, MsgId msgId = ShowAtUnreadMsgId, const QString &startToken = QString()); void joinGroupByHash(const QString &hash); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index ef6fcdba1..c98bedf48 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -2753,19 +2753,6 @@ public: _fullDisplayed = full; } -protected: - void onClickImpl() const override { - if (auto button = getButton()) { - MsgId replyTo = (_item->id > 0) ? _item->id : 0; - App::activateBotCommand(_item->history()->peer, *button, replyTo); - } - } - -private: - const HistoryItem *_item = nullptr; - int _row, _col; - bool _fullDisplayed = true; - // Finds the corresponding button in the items markup struct. // If the button is not found it returns nullptr. // Note: it is possible that we will point to the different button @@ -2782,6 +2769,16 @@ private: return nullptr; } +protected: + void onClickImpl() const override { + App::activateBotCommand(_item, _row, _col); + } + +private: + const HistoryItem *_item = nullptr; + int _row, _col; + bool _fullDisplayed = true; + // Returns the full text of the corresponding button. QString text() const { if (auto button = getButton()) { @@ -2993,6 +2990,13 @@ void ReplyKeyboard::Style::paintButton(Painter &p, const ReplyKeyboard::Button & paintButtonBg(p, rect, pressed, button.howMuchOver); paintButtonIcon(p, rect, button.type); + if (button.type == HistoryMessageReplyMarkup::Button::Callback) { + if (const HistoryMessageReplyMarkup::Button *data = button.link->getButton()) { + if (data->requestId) { + paintButtonLoading(p, rect); + } + } + } int tx = rect.x(), tw = rect.width(); if (tw > st::botKbFont->elidew + _st->padding * 2) { @@ -3024,21 +3028,21 @@ void HistoryMessageReplyMarkup::createFromButtonRows(const QVector 0) { result = std::min(result, iconWidth + 2 * int(st::msgBotKbIconPadding)); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 9cf6db042..1ad7fd6a1 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1107,6 +1107,7 @@ struct HistoryMessageReplyMarkup : public BaseComponent; using ButtonRows = QVector; @@ -1150,6 +1151,7 @@ public: protected: virtual void paintButtonBg(Painter &p, const QRect &rect, bool pressed, float64 howMuchOver) const = 0; virtual void paintButtonIcon(Painter &p, const QRect &rect, HistoryMessageReplyMarkup::Button::Type type) const = 0; + virtual void paintButtonLoading(Painter &p, const QRect &rect) const = 0; virtual int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const = 0; private: @@ -2924,6 +2926,7 @@ private: protected: void paintButtonBg(Painter &p, const QRect &rect, bool down, float64 howMuchOver) const override; void paintButtonIcon(Painter &p, const QRect &rect, HistoryMessageReplyMarkup::Button::Type type) const override; + void paintButtonLoading(Painter &p, const QRect &rect) const override; int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const override; }; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index f76585098..c027dc448 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2174,12 +2174,15 @@ void BotKeyboard::Style::paintButtonBg(Painter &p, const QRect &rect, bool down, } void BotKeyboard::Style::paintButtonIcon(Painter &p, const QRect &rect, HistoryMessageReplyMarkup::Button::Type type) const { - // they should not appear here + // Buttons with icons should not appear here. +} + +void BotKeyboard::Style::paintButtonLoading(Painter &p, const QRect &rect) const { + // Buttons with loading progress should not appear here. } int BotKeyboard::Style::minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const { int result = 2 * buttonPadding(); - // they should not appear here return result; } @@ -5161,7 +5164,9 @@ void HistoryWidget::sendBotCommand(PeerData *peer, const QString &cmd, MsgId rep App::main()->sendMessage(_history, toSend, replyTo ? ((!_peer->isUser()/* && (botStatus == 0 || botStatus == 2)*/) ? replyTo : -1) : 0, false, false); if (replyTo) { - cancelReply(); + if (_replyToId == replyTo) { + cancelReply(); + } if (_keyboard.singleUse() && _keyboard.hasMarkup() && lastKeyboardUsed) { if (_kbShown) onKbToggle(false); _history->lastKeyboardUsed = true; @@ -5171,23 +5176,37 @@ void HistoryWidget::sendBotCommand(PeerData *peer, const QString &cmd, MsgId rep _field.setFocus(); } -void HistoryWidget::sendBotCallback(PeerData *peer, const QByteArray &data, MsgId replyTo) { - if (!_peer || _peer != peer) return; +void HistoryWidget::app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, const HistoryItem *msg, int row, int col) { + if (msg->id < 0 || _peer != msg->history()->peer) { + return; + } - bool lastKeyboardUsed = (_keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId)) && (_keyboard.forMsgId() == FullMsgId(_channel, replyTo)); + bool lastKeyboardUsed = (_keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId)) && (_keyboard.forMsgId() == FullMsgId(_channel, msg->id)); - MTP::send(MTPmessages_GetBotCallbackAnswer(_peer->input, MTP_int(replyTo), MTP_bytes(data)), rpcDone(&HistoryWidget::botCallbackDone), rpcFail(&HistoryWidget::botCallbackFail)); + BotCallbackInfo info = { msg->fullId(), row, col }; + button->requestId = MTP::send(MTPmessages_GetBotCallbackAnswer(_peer->input, MTP_int(msg->id), MTP_bytes(button->data)), rpcDone(&HistoryWidget::botCallbackDone, info), rpcFail(&HistoryWidget::botCallbackFail, info)); + Ui::repaintHistoryItem(msg); - if (replyTo) { + if (_replyToId == msg->id) { cancelReply(); - if (_keyboard.singleUse() && _keyboard.hasMarkup() && lastKeyboardUsed) { - if (_kbShown) onKbToggle(false); - _history->lastKeyboardUsed = true; - } + } + if (_keyboard.singleUse() && _keyboard.hasMarkup() && lastKeyboardUsed) { + if (_kbShown) onKbToggle(false); + _history->lastKeyboardUsed = true; } } -void HistoryWidget::botCallbackDone(const MTPmessages_BotCallbackAnswer &answer) { +void HistoryWidget::botCallbackDone(BotCallbackInfo info, const MTPmessages_BotCallbackAnswer &answer, mtpRequestId req) { + if (HistoryItem *item = App::histItemById(info.msgId)) { + if (auto *markup = item->Get()) { + if (info.row < markup->rows.size() && info.col < markup->rows.at(info.row).size()) { + if (markup->rows.at(info.row).at(info.col).requestId == req) { + markup->rows.at(info.row).at(info.col).requestId = 0; + Ui::repaintHistoryItem(item); + } + } + } + } if (answer.type() == mtpc_messages_botCallbackAnswer) { const auto &answerData(answer.c_messages_botCallbackAnswer()); if (answerData.has_message()) { @@ -5196,9 +5215,20 @@ void HistoryWidget::botCallbackDone(const MTPmessages_BotCallbackAnswer &answer) } } -bool HistoryWidget::botCallbackFail(const RPCError &error) { +bool HistoryWidget::botCallbackFail(BotCallbackInfo info, const RPCError &error, mtpRequestId req) { if (mtpIsFlood(error)) return false; + if (HistoryItem *item = App::histItemById(info.msgId)) { + if (auto *markup = item->Get()) { + if (info.row < markup->rows.size() && info.col < markup->rows.at(info.row).size()) { + if (markup->rows.at(info.row).at(info.col).requestId == req) { + markup->rows.at(info.row).at(info.col).requestId = 0; + Ui::repaintHistoryItem(item); + } + } + } + } + return true; } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 8dc1a973c..88e3bfc55 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -374,6 +374,7 @@ private: protected: void paintButtonBg(Painter &p, const QRect &rect, bool down, float64 howMuchOver) const override; void paintButtonIcon(Painter &p, const QRect &rect, HistoryMessageReplyMarkup::Button::Type type) const override; + void paintButtonLoading(Painter &p, const QRect &rect) const override; int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const override; private: @@ -622,7 +623,6 @@ public: void onListEscapePressed(); void sendBotCommand(PeerData *peer, const QString &cmd, MsgId replyTo); - void sendBotCallback(PeerData *peer, const QByteArray &data, MsgId replyTo); bool insertBotCommand(const QString &cmd, bool specialGif); bool eventFilter(QObject *obj, QEvent *e) override; @@ -668,6 +668,8 @@ public: bool isItemVisible(HistoryItem *item); + void app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, const HistoryItem *msg, int row, int col); + void ui_repaintHistoryItem(const HistoryItem *item); void ui_repaintInlineItem(const InlineBots::Layout::ItemBase *gif); bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout); @@ -881,8 +883,12 @@ private: void addMessagesToFront(PeerData *peer, const QVector &messages, const QVector *collapsed); void addMessagesToBack(PeerData *peer, const QVector &messages, const QVector *collapsed); - void botCallbackDone(const MTPmessages_BotCallbackAnswer &answer); - bool botCallbackFail(const RPCError &error); + struct BotCallbackInfo { + FullMsgId msgId; + int row, col; + }; + void botCallbackDone(BotCallbackInfo info, const MTPmessages_BotCallbackAnswer &answer, mtpRequestId req); + bool botCallbackFail(BotCallbackInfo info, const RPCError &error, mtpRequestId req); enum ScrollChangeType { ScrollChangeNone, diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 4bc2b95a9..dd9928dc6 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1524,8 +1524,8 @@ void MainWidget::sendBotCommand(PeerData *peer, const QString &cmd, MsgId replyT history.sendBotCommand(peer, cmd, replyTo); } -void MainWidget::sendBotCallback(PeerData *peer, const QByteArray &data, MsgId replyTo) { - history.sendBotCallback(peer, data, replyTo); +void MainWidget::app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, const HistoryItem *msg, int row, int col) { + history.app_sendBotCallback(button, msg, row, col); } bool MainWidget::insertBotCommand(const QString &cmd, bool specialGif) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 72d17c765..5ecfa80af 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -352,7 +352,6 @@ public: void stopAnimActive(); void sendBotCommand(PeerData *peer, const QString &cmd, MsgId replyTo); - void sendBotCallback(PeerData *peer, const QByteArray &data, MsgId replyTo); bool insertBotCommand(const QString &cmd, bool specialGif); void searchMessages(const QString &query, PeerData *inPeer); @@ -442,6 +441,8 @@ public: bool isItemVisible(HistoryItem *item); + void app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, const HistoryItem *msg, int row, int col); + void ui_repaintHistoryItem(const HistoryItem *item); void ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout); bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout);