diff --git a/Telegram/Patches/qtbase_5_6_0.diff b/Telegram/Patches/qtbase_5_6_0.diff index e1e270564..6568b94b8 100644 --- a/Telegram/Patches/qtbase_5_6_0.diff +++ b/Telegram/Patches/qtbase_5_6_0.diff @@ -47,6 +47,20 @@ index 14e4fd1..c31c62b 100644 { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 }, { 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 }, { 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 }, +diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp +index b0ef2a2..7d5f7bc 100644 +--- a/src/gui/kernel/qhighdpiscaling.cpp ++++ b/src/gui/kernel/qhighdpiscaling.cpp +@@ -51,6 +51,9 @@ static const char screenFactorsEnvVar[] = "QT_SCREEN_SCALE_FACTORS"; + + static inline qreal initialGlobalScaleFactor() + { ++ // Disable environment variable dpi scaling changing. ++ // It is not supported by Telegram Desktop :( ++ return 1.; + + qreal result = 1; + if (qEnvironmentVariableIsSet(scaleFactorEnvVar)) { diff --git a/src/gui/kernel/qplatformdialoghelper.h b/src/gui/kernel/qplatformdialoghelper.h index 5b2f4ec..346a26f 100644 --- a/src/gui/kernel/qplatformdialoghelper.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 1dc366a17..d36c93ffc 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -580,6 +580,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_action_pinned_media_contact" = "a contact information"; "lng_action_pinned_media_location" = "a location mark"; "lng_action_pinned_media_sticker" = "a sticker"; +"lng_action_pinned_media_emoji_sticker" = "a {emoji} sticker"; +"lng_action_pinned_media_game" = "a game «{game}»"; "lng_action_game_score" = "{from} scored {count:#|#|#} in {game}"; "lng_profile_migrate_reached" = "{count:_not_used_|# member|# members} limit reached"; @@ -774,6 +776,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_bot_groups_not_found" = "No groups found"; "lng_bot_sure_invite" = "Add the bot to «{group}»?"; "lng_bot_already_in_group" = "The bot is already a member of the group."; +"lng_bot_choose_chat" = "Choose Chat"; +"lng_bot_no_chats" = "You have no chats"; +"lng_bot_chats_not_found" = "No chats found"; +"lng_bot_sure_share_game" = "Share the game with {user}?"; +"lng_bot_sure_share_game_group" = "Share the game with «{group}»?"; "lng_typing" = "typing"; "lng_user_typing" = "{user} is typing"; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 16c3bb700..a5985e0b2 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1856,8 +1856,9 @@ namespace { gamesData.erase(i); } convert->id = game; + convert->accessHash = 0; } - if (convert->shortName.isEmpty() && !shortName.isEmpty()) { + if (!convert->accessHash && accessHash) { convert->accessHash = accessHash; convert->shortName = textClean(shortName); convert->title = textOneLine(textClean(title)); @@ -1879,7 +1880,7 @@ namespace { } else { result = i.value(); if (result != convert) { - if (result->shortName.isEmpty() && !shortName.isEmpty()) { + if (!result->accessHash && accessHash) { result->accessHash = accessHash; result->shortName = textClean(shortName); result->title = textOneLine(textClean(title)); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 8a44c4c45..f46009d49 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -650,9 +650,17 @@ namespace Sandbox { cSetScreenScale(dbisTwo); } - if (application()->devicePixelRatio() > 1) { + auto devicePixelRatio = application()->devicePixelRatio(); + if (devicePixelRatio > 1.) { + if ((cPlatform() != dbipMac && cPlatform() != dbipMacOld) || (devicePixelRatio != 2.)) { + LOG(("Found non-trivial Device Pixel Ratio: %1").arg(devicePixelRatio)); + LOG(("Environmental variables: QT_DEVICE_PIXEL_RATIO='%1'").arg(QString::fromLatin1(qgetenv("QT_DEVICE_PIXEL_RATIO")))); + LOG(("Environmental variables: QT_SCALE_FACTOR='%1'").arg(QString::fromLatin1(qgetenv("QT_SCALE_FACTOR")))); + LOG(("Environmental variables: QT_AUTO_SCREEN_SCALE_FACTOR='%1'").arg(QString::fromLatin1(qgetenv("QT_AUTO_SCREEN_SCALE_FACTOR")))); + LOG(("Environmental variables: QT_SCREEN_SCALE_FACTORS='%1'").arg(QString::fromLatin1(qgetenv("QT_SCREEN_SCALE_FACTORS")))); + } cSetRetina(true); - cSetRetinaFactor(application()->devicePixelRatio()); + cSetRetinaFactor(devicePixelRatio); cSetIntRetinaFactor(int32(cRetinaFactor())); cSetConfigScale(dbisOne); cSetRealScale(dbisOne); diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 16f02d661..b929f3a54 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -86,6 +86,17 @@ ContactsInner::ContactsInner(ChatData *chat, MembersFilter membersFilter) : TWid init(); } +template +void ContactsInner::addDialogsToList(FilterCallback callback) { + auto v = App::main()->dialogsList(); + for_const (auto row, *v) { + auto peer = row->history()->peer; + if (callback(peer)) { + _contacts->addToEnd(row->history()); + } + } +} + ContactsInner::ContactsInner(UserData *bot) : TWidget() , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _bot(bot) @@ -93,14 +104,25 @@ ContactsInner::ContactsInner(UserData *bot) : TWidget() , _customList(std_::make_unique(Dialogs::SortMode::Add)) , _contacts(_customList.get()) , _addContactLnk(this, lang(lng_add_contact_button)) { - auto v = App::main()->dialogsList(); - for_const (auto row, *v) { - auto peer = row->history()->peer; - if (peer->isChat() && peer->asChat()->canEdit()) { - _contacts->addToEnd(row->history()); - } else if (peer->isMegagroup() && (peer->asChannel()->amCreator() || peer->asChannel()->amEditor())) { - _contacts->addToEnd(row->history()); - } + if (sharingBotGame()) { + addDialogsToList([](PeerData *peer) { + if (peer->canWrite()) { + if (auto channel = peer->asChannel()) { + return !channel->isBroadcast(); + } + return true; + } + return false; + }); + } else { + addDialogsToList([](PeerData *peer) { + if (peer->isChat() && peer->asChat()->canEdit()) { + return true; + } else if (peer->isMegagroup() && (peer->asChannel()->amCreator() || peer->asChannel()->amEditor())) { + return true; + } + return false; + }); } init(); } @@ -166,8 +188,22 @@ void ContactsInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &old } void ContactsInner::onAddBot() { - if (_bot->botInfo && !_bot->botInfo->startGroupToken.isEmpty()) { - MTP::send(MTPmessages_StartBot(_bot->inputUser, _addToPeer->input, MTP_long(rand_value()), MTP_string(_bot->botInfo->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _bot)); + if (auto &info = _bot->botInfo) { + if (!info->shareGameShortName.isEmpty()) { + MTPmessages_SendMedia::Flags sendFlags = 0; + + auto history = App::historyLoaded(_addToPeer); + auto afterRequestId = history ? history->sendRequestId : 0; + auto randomId = rand_value(); + auto requestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), _addToPeer->input, MTP_int(0), MTP_inputMediaGame(MTP_inputGameShortName(_bot->inputUser, MTP_string(info->shareGameShortName))), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, afterRequestId); + if (history) { + history->sendRequestId = requestId; + } + } else if (!info->startGroupToken.isEmpty()) { + MTP::send(MTPmessages_StartBot(_bot->inputUser, _addToPeer->input, MTP_long(rand_value()), MTP_string(info->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _bot)); + } else { + App::main()->addParticipants(_addToPeer, QVector(1, _bot)); + } } else { App::main()->addParticipants(_addToPeer, QVector(1, _bot)); } @@ -511,7 +547,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) { QString text; int32 skip = 0; if (bot()) { - text = lang(cDialogsReceived() ? lng_bot_no_groups : lng_contacts_loading); + text = lang((cDialogsReceived() && !_searching) ? (sharingBotGame() ? lng_bot_no_chats : lng_bot_no_groups) : lng_contacts_loading); } else if (_chat && _membersFilter == MembersFilterAdmins) { text = lang(lng_contacts_loading); p.fillRect(0, 0, width(), _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg); @@ -538,7 +574,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) { p.setPen(st::noContactsColor->p); QString text; if (bot()) { - text = lang(cDialogsReceived() ? lng_bot_groups_not_found : lng_contacts_loading); + text = lang((cDialogsReceived() && !_searching) ? (sharingBotGame() ? lng_bot_chats_not_found : lng_bot_groups_not_found) : lng_contacts_loading); } else if (_chat && _membersFilter == MembersFilterAdmins) { text = lang(_chat->participants.isEmpty() ? lng_contacts_loading : lng_contacts_not_found); } else { @@ -702,11 +738,22 @@ void ContactsInner::chooseParticipant() { connect(_addAdminBox, SIGNAL(confirmed()), this, SLOT(onAddAdmin())); connect(_addAdminBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoAddAdminBox(QObject*))); Ui::showLayer(_addAdminBox, KeepOtherLayers); + } else if (sharingBotGame()) { + _addToPeer = peer; + auto confirmText = [peer] { + if (peer->isUser()) { + return lng_bot_sure_share_game(lt_user, App::peerName(peer)); + } + return lng_bot_sure_share_game_group(lt_group, peer->name); + }; + auto box = std_::make_unique(confirmText()); + connect(box.get(), SIGNAL(confirmed()), this, SLOT(onAddBot())); + Ui::showLayer(box.release(), KeepOtherLayers); } else if (bot() && (peer->isChat() || peer->isMegagroup())) { _addToPeer = peer; - ConfirmBox *box = new ConfirmBox(lng_bot_sure_invite(lt_group, peer->name)); - connect(box, SIGNAL(confirmed()), this, SLOT(onAddBot())); - Ui::showLayer(box, KeepOtherLayers); + auto box = std_::make_unique(lng_bot_sure_invite(lt_group, peer->name)); + connect(box.get(), SIGNAL(confirmed()), this, SLOT(onAddBot())); + Ui::showLayer(box.release(), KeepOtherLayers); } else { Ui::hideSettingsAndLayer(true); App::main()->choosePeer(peer->id, ShowAtUnreadMsgId); @@ -899,7 +946,7 @@ void ContactsInner::updateFilter(QString filter) { _mouseSel = false; refresh(); - if (!bot() && (!_chat || _membersFilter != MembersFilterAdmins)) { + if ((!bot() || sharingBotGame()) && (!_chat || _membersFilter != MembersFilterAdmins)) { _searching = true; emit searchByUsername(); } @@ -966,6 +1013,15 @@ void ContactsInner::peopleReceived(const QString &query, const QVector } else { continue; // skip } + } else if (sharingBotGame()) { + if (!p->canWrite()) { + continue; + } + if (auto channel = p->asChannel()) { + if (channel->isBroadcast()) { + continue; + } + } } ContactData *d = new ContactData(); @@ -1032,6 +1088,10 @@ UserData *ContactsInner::bot() const { return _bot; } +bool ContactsInner::sharingBotGame() const { + return (_bot && _bot->botInfo) ? !_bot->botInfo->shareGameShortName.isEmpty() : false; +} + CreatingGroupType ContactsInner::creating() const { return _creating; } @@ -1040,8 +1100,11 @@ ContactsInner::~ContactsInner() { for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) { delete *i; } - if (_bot || (_chat && _membersFilter == MembersFilterAdmins)) { - if (_bot && _bot->botInfo) _bot->botInfo->startGroupToken = QString(); + if (_bot) { + if (auto &info = _bot->botInfo) { + info->startGroupToken = QString(); + info->shareGameShortName = QString(); + } } } @@ -1499,6 +1562,8 @@ void ContactsBox::paintEvent(QPaintEvent *e) { QString title(lang(addingAdmin ? lng_channel_add_admin : lng_profile_add_participant)); QString additional((addingAdmin || (_inner.channel() && !_inner.channel()->isMegagroup())) ? QString() : QString("%1 / %2").arg(_inner.selectedCount()).arg(Global::MegagroupSizeMax())); paintTitle(p, title, additional); + } else if (_inner.sharingBotGame()) { + paintTitle(p, lang(lng_bot_choose_chat)); } else if (_inner.bot()) { paintTitle(p, lang(lng_bot_choose_group)); } else { diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index 27a13294a..1d3976e6d 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -78,6 +78,8 @@ public: UserData *bot() const; CreatingGroupType creating() const; + bool sharingBotGame() const; + int32 selectedCount() const; bool hasAlreadyMembersInChannel() const { return !_already.isEmpty(); @@ -121,6 +123,9 @@ private: void addAdminDone(const MTPUpdates &result, mtpRequestId req); bool addAdminFail(const RPCError &error, mtpRequestId req); + template + void addDialogsToList(FilterCallback callback); + int32 _rowHeight; int _newItemHeight = 0; bool _newItemSel = false; diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp index d8ec67ce7..e845390cc 100644 --- a/Telegram/SourceFiles/boxes/report_box.cpp +++ b/Telegram/SourceFiles/boxes/report_box.cpp @@ -87,7 +87,7 @@ void ReportBox::onChange() { _reasonOtherText.destroy(); updateMaxHeight(); } - if (App::wnd()) App::wnd()->setInnerFocus(); + _reasonOtherText->setFocus(); } void ReportBox::doSetInnerFocus() { diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index 71b2d3e3a..bbf3a38dc 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -32,11 +32,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "apiwrap.h" #include "ui/toast/toast.h" +#include "history/history_media_types.h" -ShareBox::ShareBox(CopyCallback &©Callback, SubmitCallback &&submitCallback) : ItemListBox(st::boxScroll) +ShareBox::ShareBox(CopyCallback &©Callback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback) : ItemListBox(st::boxScroll) , _copyCallback(std_::move(copyCallback)) , _submitCallback(std_::move(submitCallback)) -, _inner(this) +, _inner(this, std_::move(filterCallback)) , _filter(this, st::boxSearchField, lang(lng_participant_filter)) , _filterCancel(this, st::boxSearchCancel) , _copy(this, lang(lng_share_copy_link), st::defaultBoxButton) @@ -241,7 +242,8 @@ void ShareBox::onScroll() { namespace internal { -ShareInner::ShareInner(QWidget *parent) : ScrolledWidget(parent) +ShareInner::ShareInner(QWidget *parent, ShareBox::FilterCallback &&filterCallback) : ScrolledWidget(parent) +, _filterCallback(std_::move(filterCallback)) , _chatsIndexed(std_::make_unique(Dialogs::SortMode::Add)) { _rowsTop = st::shareRowsTop; _rowHeight = st::shareRowHeight; @@ -250,7 +252,7 @@ ShareInner::ShareInner(QWidget *parent) : ScrolledWidget(parent) auto dialogs = App::main()->dialogsList(); for_const (auto row, dialogs->all()) { auto history = row->history(); - if (history->peer->canWrite()) { + if (_filterCallback(history->peer)) { _chatsIndexed->addToEnd(history); } } @@ -863,7 +865,7 @@ void ShareInner::peopleReceived(const QString &query, const QVector &pe } if (j == already) { auto *peer = App::peer(peerId); - if (!peer || !peer->canWrite()) continue; + if (!peer || !_filterCallback(peer)) continue; auto chat = new Chat(peer); updateChatName(chat, peer); @@ -958,24 +960,15 @@ void shareGameScoreFromItem(HistoryItem *item) { if (auto main = App::main()) { if (auto item = App::histItemById(data->msgId)) { if (auto bot = item->getMessageBot()) { - if (auto markup = item->Get()) { - for (int i = 0, rowsCount = markup->rows.size(); i != rowsCount; ++i) { - auto &row = markup->rows[i]; - for (int j = 0, buttonsCount = row.size(); j != buttonsCount; ++j) { - auto &button = row[j]; - if (button.type == HistoryMessageReplyMarkup::Button::Type::Game) { - auto strData = QString::fromUtf8(button.data); - auto parts = strData.split(','); - t_assert(parts.size() > 1); + if (auto media = item->getMedia()) { + if (media->type() == MediaTypeGame) { + auto shortName = static_cast(media)->game()->shortName; - QApplication::clipboard()->setText(qsl("https://telegram.me/") + bot->username + qsl("?start=") + parts[1]); + QApplication::clipboard()->setText(qsl("https://telegram.me/") + bot->username + qsl("?game=") + shortName); - Ui::Toast::Config toast; - toast.text = lang(lng_share_game_link_copied); - Ui::Toast::Show(App::wnd(), toast); - return; - } - } + Ui::Toast::Config toast; + toast.text = lang(lng_share_game_link_copied); + Ui::Toast::Show(App::wnd(), toast); } } } @@ -1015,7 +1008,16 @@ void shareGameScoreFromItem(HistoryItem *item) { } } }; - Ui::showLayer(new ShareBox(std_::move(copyCallback), std_::move(submitCallback))); + auto filterCallback = [](PeerData *peer) { + if (peer->canWrite()) { + if (auto channel = peer->asChannel()) { + return !channel->isBroadcast(); + } + return true; + } + return false; + }; + Ui::showLayer(new ShareBox(std_::move(copyCallback), std_::move(submitCallback), std_::move(filterCallback))); } } // namespace diff --git a/Telegram/SourceFiles/boxes/sharebox.h b/Telegram/SourceFiles/boxes/sharebox.h index 11288b6d8..7f7d8e99d 100644 --- a/Telegram/SourceFiles/boxes/sharebox.h +++ b/Telegram/SourceFiles/boxes/sharebox.h @@ -47,7 +47,8 @@ class ShareBox : public ItemListBox, public RPCSender { public: using CopyCallback = base::lambda_unique; using SubmitCallback = base::lambda_unique &)>; - ShareBox(CopyCallback &©Callback, SubmitCallback &&submitCallback); + using FilterCallback = base::lambda_unique; + ShareBox(CopyCallback &©Callback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback); private slots: void onFilterUpdate(); @@ -112,7 +113,7 @@ class ShareInner : public ScrolledWidget, public RPCSender, private base::Subscr Q_OBJECT public: - ShareInner(QWidget *parent); + ShareInner(QWidget *parent, ShareBox::FilterCallback &&filterCallback); QVector selected() const; bool hasSelected() const; @@ -197,6 +198,7 @@ private: int _active = -1; int _upon = -1; + ShareBox::FilterCallback _filterCallback; std_::unique_ptr _chatsIndexed; QString _filter; using FilteredDialogs = QVector; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 56b7b974e..5a2f73667 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -881,6 +881,10 @@ HistoryItem *History::createItemPhoto(MsgId id, MTPDmessage::Flags flags, int32 return HistoryMessage::create(this, id, flags, replyTo, viaBotId, date, from, photo, caption, markup); } +HistoryItem *History::createItemGame(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup) { + return HistoryMessage::create(this, id, flags, replyTo, viaBotId, date, from, game, markup); +} + HistoryItem *History::addNewService(MsgId msgId, QDateTime date, const QString &text, MTPDmessage::Flags flags, bool newMsg) { return addNewItem(HistoryService::create(this, msgId, date, text, flags), newMsg); } @@ -928,6 +932,10 @@ HistoryItem *History::addNewPhoto(MsgId id, MTPDmessage::Flags flags, int32 viaB return addNewItem(createItemPhoto(id, flags, viaBotId, replyTo, date, from, photo, caption, markup), true); } +HistoryItem *History::addNewGame(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup) { + return addNewItem(createItemGame(id, flags, viaBotId, replyTo, date, from, game, markup), true); +} + bool History::addToOverview(MediaOverviewType type, MsgId msgId, AddToOverviewMethod method) { bool adding = false; switch (method) { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 02af01452..c9d44ebfb 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -205,6 +205,7 @@ public: HistoryItem *addNewForwarded(MsgId id, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *item); HistoryItem *addNewDocument(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup); HistoryItem *addNewPhoto(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); + HistoryItem *addNewGame(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup); void addOlderSlice(const QVector &slice); void addNewerSlice(const QVector &slice); @@ -463,6 +464,7 @@ protected: HistoryItem *createItemForwarded(MsgId id, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *msg); HistoryItem *createItemDocument(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup); HistoryItem *createItemPhoto(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); + HistoryItem *createItemGame(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup); HistoryItem *addNewItem(HistoryItem *adding, bool newMsg); HistoryItem *addNewInTheMiddle(HistoryItem *newItem, int32 blockIndex, int32 itemIndex); diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index d2e371f0f..5cb96d971 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -69,7 +69,7 @@ inline void initTextOptions() { bool needReSetInlineResultDocument(const MTPMessageMedia &media, DocumentData *existing) { if (media.type() == mtpc_messageMediaDocument) { - if (DocumentData *document = App::feedDocument(media.c_messageMediaDocument().vdocument)) { + if (auto document = App::feedDocument(media.c_messageMediaDocument().vdocument)) { if (document == existing) { return false; } else { @@ -1964,8 +1964,6 @@ private: } // namespace HistorySticker::HistorySticker(HistoryItem *parent, DocumentData *document) : HistoryMedia(parent) -, _pixw(1) -, _pixh(1) , _data(document) , _emoji(_data->sticker()->alt) { _data->thumb->load(); @@ -3103,11 +3101,12 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, uint p.translate(attachLeft, attachTop); _attach->draw(p, r.translated(-attachLeft, -attachTop), attachSelection, ms); auto pixwidth = _attach->currentWidth(); + auto pixheight = _attach->height(); - auto gameX = st::msgDateImgDelta; - auto gameY = st::msgDateImgDelta; auto gameW = _gameTagWidth + 2 * st::msgDateImgPadding.x(); auto gameH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y(); + auto gameX = pixwidth - st::msgDateImgDelta - gameW; + auto gameY = pixheight - st::msgDateImgDelta - gameH; App::roundRect(p, rtlrect(gameX, gameY, gameW, gameH, pixwidth), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); @@ -3214,12 +3213,25 @@ void HistoryGame::detachFromParent() { if (_attach) _attach->detachFromParent(); } -QString HistoryGame::notificationText() const { - return _data->title; +void HistoryGame::updateSentMedia(const MTPMessageMedia &media) { + if (media.type() == mtpc_messageMediaGame) { + auto &game = media.c_messageMediaGame().vgame; + if (game.type() == mtpc_game) { + App::feedGame(game.c_game(), _data); + } + } } -QString HistoryGame::inDialogsText() const { - return textcmdLink(1, _data->title); +bool HistoryGame::needReSetInlineResultMedia(const MTPMessageMedia &media) { + updateSentMedia(media); + return false; +} + +QString HistoryGame::notificationText() const { + QString result; // add a game controller emoji before game title + result.reserve(_data->title.size() + 3); + result.append(QChar(0xD83C)).append(QChar(0xDFAE)).append(QChar(' ')).append(_data->title); + return result; } TextWithEntities HistoryGame::selectedText(TextSelection selection) const { diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index 2d457231c..fe62bcb08 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -552,6 +552,9 @@ public: bool customInfoLayout() const override { return true; } + QString emoji() const { + return _emoji; + } private: int additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const; @@ -560,7 +563,8 @@ private: } QString toString() const; - int16 _pixw, _pixh; + int16 _pixw = 1; + int16 _pixh = 1; ClickHandlerPtr _packLink; DocumentData *_data; QString _emoji; @@ -762,7 +766,6 @@ public: } QString notificationText() const override; - QString inDialogsText() const override; TextWithEntities selectedText(TextSelection selection) const override; void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; @@ -793,6 +796,9 @@ public: return _data; } + void updateSentMedia(const MTPMessageMedia &media) override; + bool needReSetInlineResultMedia(const MTPMessageMedia &media) override; + bool needsBubble() const override { return true; } diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 99def6c55..cf0894118 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -49,7 +49,7 @@ MediaOverviewType messageMediaToOverviewType(HistoryMedia *media) { case MediaTypePhoto: return OverviewPhotos; case MediaTypeVideo: return OverviewVideos; case MediaTypeFile: return OverviewFiles; - case MediaTypeMusicFile: return media->getDocument()->isMusic() ? OverviewMusicFiles : OverviewFiles; + case MediaTypeMusicFile: return media->getDocument()->isMusic() ? OverviewMusicFiles : OverviewCount; case MediaTypeVoiceFile: return OverviewVoiceFiles; case MediaTypeGif: return media->getDocument()->isGifv() ? OverviewCount : OverviewFiles; default: break; @@ -465,6 +465,14 @@ HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags setText(TextWithEntities()); } +HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup) + : HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) { + createComponentsHelper(flags, replyTo, viaBotId, markup); + + _media.reset(new HistoryGame(this, game)); + setText(TextWithEntities()); +} + void HistoryMessage::createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, const MTPReplyMarkup &markup) { CreateConfig config; @@ -1899,7 +1907,7 @@ bool HistoryService::preparePinnedText(const QString &from, QString *outText, Li ClickHandlerPtr second; auto pinned = Get(); if (pinned && pinned->msg) { - HistoryMedia *media = pinned->msg->getMedia(); + auto media = pinned->msg->getMedia(); QString mediaText; switch (media ? media->type() : MediaTypeCount) { case MediaTypePhoto: mediaText = lang(lng_action_pinned_media_photo); break; @@ -1907,10 +1915,21 @@ bool HistoryService::preparePinnedText(const QString &from, QString *outText, Li case MediaTypeContact: mediaText = lang(lng_action_pinned_media_contact); break; case MediaTypeFile: mediaText = lang(lng_action_pinned_media_file); break; case MediaTypeGif: mediaText = lang(lng_action_pinned_media_gif); break; - case MediaTypeSticker: mediaText = lang(lng_action_pinned_media_sticker); break; + case MediaTypeSticker: { + auto emoji = static_cast(media)->emoji(); + if (emoji.isEmpty()) { + mediaText = lang(lng_action_pinned_media_sticker); + } else { + mediaText = lng_action_pinned_media_emoji_sticker(lt_emoji, emoji); + } + } break; case MediaTypeLocation: mediaText = lang(lng_action_pinned_media_location); break; case MediaTypeMusicFile: mediaText = lang(lng_action_pinned_media_audio); break; case MediaTypeVoiceFile: mediaText = lang(lng_action_pinned_media_voice); break; + case MediaTypeGame: { + auto title = static_cast(media)->game()->title; + mediaText = lng_action_pinned_media_game(lt_game, title); + } break; } if (mediaText.isEmpty()) { QString original = pinned->msg->originalText().text; diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 1bcd8c4e5..bfefaec4c 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -39,6 +39,9 @@ public: static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup) { return _create(history, msgId, flags, replyTo, viaBotId, date, from, photo, caption, markup); } + static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup) { + return _create(history, msgId, flags, replyTo, viaBotId, date, from, game, markup); + } void initTime(); void initMedia(const MTPMessageMedia *media); @@ -164,6 +167,7 @@ private: HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const TextWithEntities &textWithEntities); // local message HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup); // local document HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); // local photo + HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup); // local game friend class HistoryItemInstantiated; void setEmptyText(); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 63385e707..fac4a6cff 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -134,7 +134,7 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons bool loaded = document->loaded(), loading = document->loading(), displayLoading = document->displayLoading(); if (loaded && !gif() && _gif != Media::Clip::BadReader) { - Gif *that = const_cast(this); + auto that = const_cast(this); that->_gif = new Media::Clip::Reader(document->location(), document->data(), [that](Media::Clip::Notification notification) { that->clipCallback(notification); }); @@ -276,7 +276,6 @@ QSize Gif::countFrameSize() const { Gif::~Gif() { if (gif()) deleteAndMark(_gif); - deleteAndMark(_animation); } void Gif::prepareThumb(int32 width, int32 height, const QSize &frame) const { @@ -306,7 +305,7 @@ void Gif::prepareThumb(int32 width, int32 height, const QSize &frame) const { void Gif::ensureAnimation() const { if (!_animation) { - _animation = new AnimationData(animation(const_cast(this), &Gif::step_radial)); + _animation = std_::make_unique(animation(const_cast(this), &Gif::step_radial)); } } @@ -324,8 +323,7 @@ void Gif::step_radial(uint64 ms, bool timer) { DocumentData *document = getShownDocument(); _animation->radial.update(document->progress(), !document->loading() || document->loaded(), ms); if (!_animation->radial.animating() && document->loaded()) { - delete _animation; - _animation = nullptr; + _animation.reset(); } } } @@ -1140,6 +1138,239 @@ void Article::prepareThumb(int width, int height) const { } } +Game::Game(Result *result) : ItemBase(result) +, _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) +, _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) { + countFrameSize(); +} + +void Game::countFrameSize() { + if (auto document = getResultDocument()) { + if (document->isAnimation()) { + auto documentSize = document->dimensions; + if (documentSize.isEmpty()) { + documentSize = QSize(st::inlineThumbSize, st::inlineThumbSize); + } + auto resizeByHeight1 = (documentSize.width() > documentSize.height()) && (documentSize.height() >= st::inlineThumbSize); + auto resizeByHeight2 = (documentSize.height() >= documentSize.width()) && (documentSize.width() < st::inlineThumbSize); + if (resizeByHeight1 || resizeByHeight2) { + if (documentSize.height() > st::inlineThumbSize) { + _frameSize = QSize((documentSize.width() * st::inlineThumbSize) / documentSize.height(), st::inlineThumbSize); + } + } else { + if (documentSize.width() > st::inlineThumbSize) { + _frameSize = QSize(st::inlineThumbSize, (documentSize.height() * st::inlineThumbSize) / documentSize.width()); + } + } + if (!_frameSize.width()) { + _frameSize.setWidth(1); + } + if (!_frameSize.height()) { + _frameSize.setHeight(1); + } + } + } +} + +void Game::initDimensions() { + _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; + int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip); + TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; + _title.setText(st::semiboldFont, textOneLine(_result->getLayoutTitle()), titleOpts); + int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height); + + int32 descriptionLines = 2; + QString description = _result->getLayoutDescription(); + TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, descriptionLines * st::normalFont->height, Qt::LayoutDirectionAuto }; + _description.setText(st::normalFont, description, descriptionOpts); + int32 descriptionHeight = qMin(_description.countHeight(_maxw), descriptionLines * st::normalFont->height); + + _minh = titleHeight + descriptionHeight; + accumulate_max(_minh, st::inlineThumbSize); + _minh += st::inlineRowMargin * 2 + st::inlineRowBorder; +} + +void Game::setPosition(int32 position) { + ItemBase::setPosition(position); + if (_position < 0) { + if (gif()) delete _gif; + _gif = 0; + } +} + +void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) const { + int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft; + + left = st::inlineThumbSize + st::inlineThumbSkip; + auto rthumb = rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width); + + // Gif thumb + auto thumbDisplayed = false, radial = false; + auto document = getResultDocument(); + auto animatedThumb = document && document->isAnimation(); + if (animatedThumb) { + document->automaticLoad(nullptr); + + bool loaded = document->loaded(), loading = document->loading(), displayLoading = document->displayLoading(); + if (loaded && !gif() && _gif != Media::Clip::BadReader) { + auto that = const_cast(this); + that->_gif = new Media::Clip::Reader(document->location(), document->data(), [that](Media::Clip::Notification notification) { + that->clipCallback(notification); + }); + if (gif()) _gif->setAutoplay(); + } + + bool animating = (gif() && _gif->started()); + if (displayLoading) { + if (!_radial) { + _radial = std_::make_unique(animation(const_cast(this), &Game::step_radial)); + } + if (!_radial->animating()) { + _radial->start(document->progress()); + } + } + radial = isRadialAnimation(context->ms); + + if (animating) { + if (!_thumb.isNull()) _thumb = QPixmap(); + auto animationThumb = _gif->current(_frameSize.width(), _frameSize.height(), st::inlineThumbSize, st::inlineThumbSize, context->paused ? 0 : context->ms); + p.drawPixmapLeft(rthumb.topLeft(), _width, animationThumb); + thumbDisplayed = true; + } + } + + if (!thumbDisplayed) { + prepareThumb(st::inlineThumbSize, st::inlineThumbSize); + if (_thumb.isNull()) { + p.fillRect(rthumb, st::overviewPhotoBg); + } else { + p.drawPixmapLeft(rthumb.topLeft(), _width, _thumb); + } + } + + if (radial) { + p.fillRect(rthumb, st::msgDateImgBg); + QRect inner((st::inlineThumbSize - st::msgFileSize) / 2, (st::inlineThumbSize - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); + if (radial) { + p.setOpacity(1); + QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); + _radial->draw(p, rinner, st::msgFileRadialLine, st::msgInBg); + } + } + + p.setPen(st::black); + _title.drawLeftElided(p, left, st::inlineRowMargin, _width - left, _width, 2); + int32 titleHeight = qMin(_title.countHeight(_width - left), st::semiboldFont->height * 2); + + p.setPen(st::inlineDescriptionFg); + int32 descriptionLines = 2; + _description.drawLeftElided(p, left, st::inlineRowMargin + titleHeight, _width - left, _width, descriptionLines); + + if (!context->lastRow) { + p.fillRect(rtlrect(left, _height - st::inlineRowBorder, _width - left, st::inlineRowBorder, _width), st::inlineRowBorderFg); + } +} + +void Game::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const { + int left = st::inlineThumbSize + st::inlineThumbSkip; + if (x >= 0 && x < left - st::inlineThumbSkip && y >= st::inlineRowMargin && y < st::inlineRowMargin + st::inlineThumbSize) { + link = _send; + return; + } + if (x >= left && x < _width && y >= 0 && y < _height) { + link = _send; + return; + } +} + +void Game::prepareThumb(int width, int height) const { + auto thumb = ([this]() { + if (auto photo = getResultPhoto()) { + return photo->medium; + } else if (auto document = getResultDocument()) { + return document->thumb; + } + return ImagePtr(); + })(); + if (thumb->isNull()) { + return; + } + + if (thumb->loaded()) { + if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { + int w = qMax(convertScale(thumb->width()), 1), h = qMax(convertScale(thumb->height()), 1); + auto resizeByHeight1 = (w * height > h * width) && (h >= height); + auto resizeByHeight2 = (h * width >= w * height) && (w < width); + if (resizeByHeight1 || resizeByHeight2) { + if (h > height) { + w = w * height / h; + h = height; + } + } else { + if (w > width) { + h = h * width / w; + w = width; + } + } + _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixSmooth, width, height); + } + } else { + thumb->load(); + } +} + +bool Game::isRadialAnimation(uint64 ms) const { + if (!_radial || !_radial->animating()) return false; + + _radial->step(ms); + return _radial && _radial->animating(); +} + +void Game::step_radial(uint64 ms, bool timer) { + if (timer) { + update(); + } else { + auto document = getResultDocument(); + _radial->update(document->progress(), !document->loading() || document->loaded(), ms); + if (!_radial->animating() && document->loaded()) { + _radial.reset(); + } + } +} + +void Game::clipCallback(Media::Clip::Notification notification) { + using namespace Media::Clip; + switch (notification) { + case NotificationReinit: { + if (gif()) { + if (_gif->state() == State::Error) { + delete _gif; + _gif = BadReader; + getResultDocument()->forget(); + } else if (_gif->ready() && !_gif->started()) { + _gif->start(_frameSize.width(), _frameSize.height(), st::inlineThumbSize, st::inlineThumbSize, ImageRoundRadius::None); + } else if (_gif->autoPausedGif() && !Ui::isInlineItemVisible(this)) { + delete _gif; + _gif = nullptr; + getResultDocument()->forget(); + } + } + + update(); + } break; + + case NotificationRepaint: { + if (gif() && !_gif->currentDisplayed()) { + update(); + } + } break; + } +} + +Game::~Game() { + if (gif()) deleteAndMark(_gif); +} + } // namespace internal } // namespace Layout } // namespace InlineBots diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h index d151cb86b..d9327d9d5 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h @@ -116,7 +116,7 @@ private: FloatAnimation _a_over; Ui::RadialAnimation radial; }; - mutable AnimationData *_animation = nullptr; + mutable std_::unique_ptr _animation; mutable FloatAnimation _a_deleteOver; }; @@ -339,6 +339,40 @@ private: }; +class Game : public ItemBase { +public: + Game(Result *result); + + void setPosition(int32 position) override; + void initDimensions() override; + + void paint(Painter &p, const QRect &clip, const PaintContext *context) const override; + void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const override; + + ~Game(); + +private: + void countFrameSize(); + + bool gif() const { + return (!_gif || _gif == Media::Clip::BadReader) ? false : true; + } + void prepareThumb(int32 width, int32 height) const; + + bool isRadialAnimation(uint64 ms) const; + void step_radial(uint64 ms, bool timer); + + void clipCallback(Media::Clip::Notification notification); + + Media::Clip::Reader *_gif = nullptr; + mutable QPixmap _thumb; + mutable std_::unique_ptr _radial; + Text _title, _description; + + QSize _frameSize; + +}; + } // namespace internal } // namespace Layout } // namespace InlineBots diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp index af03a20f0..bceb885b1 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp @@ -113,6 +113,7 @@ std_::unique_ptr ItemBase::createLayout(Result *result, bool forceThum case Type::Article: case Type::Geo: case Type::Venue: return std_::make_unique(result, forceThumb); break; + case Type::Game: return std_::make_unique(result); break; case Type::Contact: return std_::make_unique(result); break; } return std_::unique_ptr(); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index d3afc27b4..ab9824bb3 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -47,6 +47,7 @@ std_::unique_ptr Result::create(uint64 queryId, const MTPBotInlineResult result->insert(qsl("contact"), Result::Type::Contact); result->insert(qsl("venue"), Result::Type::Venue); result->insert(qsl("geo"), Result::Type::Geo); + result->insert(qsl("game"), Result::Type::Game); return result.release(); })() }; @@ -122,6 +123,9 @@ std_::unique_ptr Result::create(uint64 queryId, const MTPBotInlineResult if (result->_type == Type::Photo) { result->createPhoto(); result->sendData.reset(new internal::SendPhoto(result->_photo, qs(r.vcaption))); + } else if (result->_type == Type::Game) { + result->createGame(); + result->sendData.reset(new internal::SendGame(result->_game)); } else { result->createDocument(); result->sendData.reset(new internal::SendFile(result->_document, qs(r.vcaption))); @@ -313,7 +317,7 @@ void Result::createPhoto() { ImagePtr medium = ImagePtr(mediumsize.width(), mediumsize.height()); ImagePtr full = ImagePtr(_content_url, _width, _height); - uint64 photoId = rand_value(); + auto photoId = rand_value(); _photo = App::photoSet(photoId, 0, 0, unixtime(), _thumb, medium, full); _photo->thumb = _thumb; } @@ -321,8 +325,6 @@ void Result::createPhoto() { void Result::createDocument() { if (_document) return; - uint64 docId = rand_value(); - if (!_thumb_url.isEmpty()) { _thumb = ImagePtr(_thumb_url, QSize(90, 90)); } @@ -352,16 +354,16 @@ void Result::createDocument() { attributes.push_back(MTP_documentAttributeAudio(MTP_flags(flags), MTP_int(_duration), MTPstring(), MTPstring(), MTPbytes())); } - MTPDocument document = MTP_document(MTP_long(docId), MTP_long(0), MTP_int(unixtime()), MTP_string(mime), MTP_int(0), MTP_photoSizeEmpty(MTP_string("")), MTP_int(MTP::maindc()), MTP_int(0), MTP_vector(attributes)); - - _document = App::feedDocument(document); + auto documentId = rand_value(); + _document = App::documentSet(documentId, nullptr, 0, 0, unixtime(), attributes, mime, _thumb, MTP::maindc(), 0, StorageImageLocation()); _document->setContentUrl(_content_url); - if (!_thumb->isNull()) { - _document->thumb = _thumb; - } } -Result::~Result() { +void Result::createGame() { + if (_game) return; + + auto gameId = rand_value(); + _game = App::gameSet(gameId, nullptr, 0, QString(), _title, _description, _photo, _document); } } // namespace InlineBots diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.h b/Telegram/SourceFiles/inline_bots/inline_bot_result.h index 105b50b46..09c58aad9 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.h @@ -74,11 +74,10 @@ public: QString getLayoutTitle() const; QString getLayoutDescription() const; - ~Result(); - private: void createPhoto(); void createDocument(); + void createGame(); enum class Type { Unknown, @@ -92,6 +91,7 @@ private: Contact, Geo, Venue, + Game, }; friend class internal::SendData; @@ -112,6 +112,7 @@ private: DocumentData *_document = nullptr; PhotoData *_photo = nullptr; + GameData *_game = nullptr; std_::unique_ptr _mtpKeyboard; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp index d2f5354ec..c086bec36 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp @@ -90,5 +90,11 @@ UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const { history->addNewDocument(msgId, flags, viaBotId, replyToId, date(mtpDate), fromId, _document, _caption, markup); } +void SendGame::addToHistory(const Result *owner, History *history, + MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, + UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const { + history->addNewGame(msgId, flags, viaBotId, replyToId, date(mtpDate), fromId, _game, markup); +} + } // 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 3c2e8fbf4..4b0c76ce4 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h @@ -221,5 +221,25 @@ private: }; +// Message with game. +class SendGame : public SendData { +public: + SendGame(GameData *game) + : _game(game) { + } + + bool isValid() const override { + return _game != nullptr; + } + + void addToHistory(const Result *owner, History *history, + MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, + UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const override; + +private: + GameData *_game; + +}; + } // namespace internal } // namespace InlineBots diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index cb6b37fe9..37ac2441b 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -3349,7 +3349,7 @@ void MainWidget::openLocalUrl(const QString &url) { } else if (auto usernameMatch = regex_match(qsl("^resolve/?\\?(.+)(#|$)"), command, matchOptions)) { auto params = url_parse_params(usernameMatch->captured(1), UrlParamNameTransform::ToLower); auto domain = params.value(qsl("domain")); - if (auto domainMatch = regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), domain, matchOptions)) { + if (regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), domain, matchOptions)) { auto start = qsl("start"); auto startToken = params.value(start); if (startToken.isEmpty()) { @@ -3364,6 +3364,11 @@ void MainWidget::openLocalUrl(const QString &url) { if (auto postId = postParam.toInt()) { post = postId; } + auto gameParam = params.value(qsl("game")); + if (!gameParam.isEmpty() && regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), gameParam, matchOptions)) { + startToken = gameParam; + post = ShowAtGameShareMsgId; + } openPeerByName(domain, post, startToken); } } else if (auto shareGameScoreMatch = regex_match(qsl("^share_game_score/?\\?(.+)(#|$)"), command, matchOptions)) { @@ -3377,7 +3382,14 @@ void MainWidget::openPeerByName(const QString &username, MsgId msgId, const QStr PeerData *peer = App::peerByName(username); if (peer) { - if (msgId == ShowAtProfileMsgId && !peer->isChannel()) { + if (msgId == ShowAtGameShareMsgId) { + if (peer->isUser() && peer->asUser()->botInfo && !startToken.isEmpty()) { + peer->asUser()->botInfo->shareGameShortName = startToken; + Ui::showLayer(new ContactsBox(peer->asUser())); + } else { + Ui::showPeerHistoryAsync(peer->id, ShowAtUnreadMsgId, Ui::ShowWay::Forward); + } + } else if (msgId == ShowAtProfileMsgId && !peer->isChannel()) { if (peer->isUser() && peer->asUser()->botInfo && !peer->asUser()->botInfo->cantJoinGroups && !startToken.isEmpty()) { peer->asUser()->botInfo->startGroupToken = startToken; Ui::showLayer(new ContactsBox(peer->asUser())); diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 12545ed15..babee7e99 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -163,6 +163,7 @@ constexpr const MsgId ShowAtTheEndMsgId = -0x40000000; constexpr const MsgId SwitchAtTopMsgId = -0x3FFFFFFF; constexpr const MsgId ShowAtProfileMsgId = -0x3FFFFFFE; constexpr const MsgId ShowAndStartBotMsgId = -0x3FFFFFD; +constexpr const MsgId ShowAtGameShareMsgId = -0x3FFFFFC; constexpr const MsgId ServerMaxMsgId = 0x3FFFFFFF; constexpr const MsgId ShowAtUnreadMsgId = 0; @@ -385,7 +386,7 @@ struct BotInfo { QList commands; Text text = Text{ int(st::msgMinWidth) }; // description - QString startToken, startGroupToken; + QString startToken, startGroupToken, shareGameShortName; PeerId inlineReturnPeerId = 0; }; @@ -1141,7 +1142,7 @@ public: return (type == AnimatedDocument) && !mime.compare(qstr("video/mp4"), Qt::CaseInsensitive); } bool isMusic() const { - return (type == SongDocument) ? !static_cast(_additional.get())->title.isEmpty() : false; + return (type == SongDocument) ? (static_cast(_additional.get())->duration != 0) : false; } bool isVideo() const { return (type == VideoDocument);