diff --git a/Telegram/PrepareWin.bat b/Telegram/PrepareWin.bat index 3ce0aef9b1..4bde9bfb09 100644 --- a/Telegram/PrepareWin.bat +++ b/Telegram/PrepareWin.bat @@ -1,9 +1,9 @@ @echo OFF -set "AppVersion=8026" -set "AppVersionStrSmall=0.8.26" -set "AppVersionStr=0.8.26" -set "AppVersionStrFull=0.8.26.0" +set "AppVersion=8027" +set "AppVersionStrSmall=0.8.27" +set "AppVersionStr=0.8.27" +set "AppVersionStrFull=0.8.27.0" set "DevChannel=1" if %DevChannel% neq 0 goto preparedev diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 4cdc173e83..61e616352d 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1011,8 +1011,8 @@ recordSignalMin: 5px; recordSignalMax: 10px; recordCancel: #aaa; recordCancelActive: #ec6466; -recordFont: font(16px); -recordTextTop: 12px; +recordFont: font(13px); +recordTextTop: 14px; replySkip: 51px; replyColor: #377aae; @@ -1679,7 +1679,7 @@ botKbColor: #8a8a8f; botKbFont: font(16px); botKbButton: botKeyboardButton { margin: 10px; - padding: 14px; + padding: 10px; height: 46px; textTop: 13px; downTextTop: 14px; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index af1927b43e..37ce64fcc2 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1977,12 +1977,15 @@ namespace App { void feedReplyMarkup(MsgId msgId, const MTPReplyMarkup &markup) { ReplyMarkup data; + ReplyMarkup::Commands &commands(data.commands); switch (markup.type()) { case mtpc_replyKeyboardMarkup: { const MTPDreplyKeyboardMarkup &d(markup.c_replyKeyboardMarkup()); + data.flags = d.vflags.v; + const QVector &v(d.vrows.c_vector().v); if (!v.isEmpty()) { - data.reserve(v.size()); + commands.reserve(v.size()); for (int32 i = 0, l = v.size(); i < l; ++i) { switch (v.at(i).type()) { case mtpc_keyboardButtonRow: { @@ -1998,12 +2001,12 @@ namespace App { } break; } } - if (!btns.isEmpty()) data.push_back(btns); + if (!btns.isEmpty()) commands.push_back(btns); } } break; } } - if (!data.isEmpty()) { + if (!commands.isEmpty()) { replyMarkups.insert(msgId, data); } } @@ -2016,7 +2019,7 @@ namespace App { } const ReplyMarkup &replyMarkup(MsgId msgId) { - static ReplyMarkup zeroMarkup; + static ReplyMarkup zeroMarkup(MTPDreplyKeyboardMarkup_flag_ZERO); ReplyMarkups::const_iterator i = replyMarkups.constFind(msgId); if (i == replyMarkups.cend()) return zeroMarkup; return i.value(); @@ -2046,15 +2049,21 @@ namespace App { } } + void insertBotCommand(const QString &cmd) { + if (App::main()) { + App::main()->insertBotCommand(cmd); + } + } + void searchByHashtag(const QString &tag) { if (App::main()) { App::main()->searchMessages(tag + ' '); } } - void openUserByName(const QString &username, bool toProfile, const QString &start, const QString &startToken) { + void openUserByName(const QString &username, bool toProfile, const QString &startToken) { if (App::main()) { - App::main()->openUserByName(username, toProfile, start, startToken); + App::main()->openUserByName(username, toProfile, startToken); } } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 8320a14028..c76ac59acb 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -35,7 +35,13 @@ typedef QHash VideoItems; typedef QHash AudioItems; typedef QHash DocumentItems; typedef QHash WebPageItems; -typedef QList > ReplyMarkup; +struct ReplyMarkup { + ReplyMarkup(int32 flags = 0) : flags(flags) { + } + typedef QList > Commands; + Commands commands; + int32 flags; +}; enum RoundCorners { MaskCorners = 0x00, // for images @@ -234,8 +240,9 @@ namespace App { void setProxySettings(QTcpSocket &socket); void sendBotCommand(const QString &cmd, MsgId replyTo = 0); + void insertBotCommand(const QString &cmd); void searchByHashtag(const QString &tag); - void openUserByName(const QString &username, bool toProfile = false, const QString &start = QString(), const QString &startToken = QString()); + void openUserByName(const QString &username, bool toProfile = false, const QString &startToken = QString()); void joinGroupByHash(const QString &hash); void stickersBox(const QString &name); void openLocalUrl(const QString &url); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 0a4fdf100e..58279717f0 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -640,7 +640,7 @@ void Application::checkMapVersion() { psRegisterCustomScheme(); if (Local::oldMapVersion()) { QString versionFeatures; - if (DevChannel && Local::oldMapVersion() < 8026) { + if (DevChannel && Local::oldMapVersion() < 8027) { versionFeatures = lang(lng_new_version_minor);// QString::fromUtf8("\xe2\x80\x94 IPv6 connections support\n\xe2\x80\x94 Bug fixes and minor stuff");// .replace('@', qsl("@") + QChar(0x200D)); } else if (!DevChannel && Local::oldMapVersion() < 8024) { versionFeatures = lng_new_version_text(lt_blog_link, qsl("https://telegram.org/blog/bot-revolution"));// lang(lng_new_version_text).trimmed(); diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 579a0cd07d..4b6a1403d6 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -91,7 +91,15 @@ void ContactsInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &old } void ContactsInner::onAddBot() { - App::main()->addParticipants(_addToChat, QVector(1, _bot)); + if (_bot->botInfo && !_bot->botInfo->startGroupToken.isEmpty()) { + uint64 randomId = MTP::nonce(); + MTP::send(MTPmessages_StartBot(_bot->inputUser, MTP_int(App::chatFromPeer(_addToChat->id)), MTP_long(randomId), MTP_string(_bot->botInfo->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _bot)); + + App::wnd()->hideLayer(); + App::main()->showPeer(_addToChat->id, 0, false); + } else { + App::main()->addParticipants(_addToChat, QVector(1, _bot)); + } } void ContactsInner::peerUpdated(PeerData *peer) { @@ -738,7 +746,10 @@ ContactsInner::~ContactsInner() { for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) { delete *i; } - if (_bot) delete _contacts; + if (_bot) { + delete _contacts; + if (_bot->botInfo) _bot->botInfo->startGroupToken = QString(); + } } void ContactsInner::resizeEvent(QResizeEvent *e) { diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 84d1f1b41a..b90984726d 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org */ #pragma once -static const int32 AppVersion = 8026; -static const wchar_t *AppVersionStr = L"0.8.26"; +static const int32 AppVersion = 8027; +static const wchar_t *AppVersionStr = L"0.8.27"; static const bool DevChannel = true; static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 5c566bed77..ca2bcdbc3e 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -2411,7 +2411,7 @@ void MentionsInner::paintEvent(QPaintEvent *e) { int32 atwidth = st::mentionFont->m.width('@'), hashwidth = st::mentionFont->m.width('#'); int32 availwidth = width() - 2 * st::mentionPadding.left() - st::mentionPhotoSize - 2 * st::mentionPadding.right(); - int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::dlgShadow, htagwidth = width() - st::mentionPadding.right() - htagleft; + int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::dlgShadow, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width; int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1; int32 last = _rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size(); diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index 3665d66d3f..ccc2662b25 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -30,7 +30,7 @@ namespace { const QRegularExpression _reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@")); const QRegularExpression _reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[\\w]{2,64}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption); const QRegularExpression _reMention(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{5,32}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption); - const QRegularExpression _reBotCommand(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])/[\\w]{1,64}(@[A-Za-z_0-9]{5,32})?([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption); + const QRegularExpression _reBotCommand(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])/[A-Za-z_0-9]{1,64}(@[A-Za-z_0-9]{5,32})?([\\W]|$)")); QSet _validProtocols, _validTopDomains; const style::textStyle *_textStyle = 0; @@ -757,7 +757,7 @@ void TextLink::onClick(Qt::MouseButton button) const { start = (start == qsl("sg") ? qsl("startgroup") : (start == qsl("s") ? qsl("start") : QString())); } } - App::openUserByName(telegramMeUser.captured(1), start == qsl("startgroup"), start, startToken); + App::openUserByName(telegramMeUser.captured(1), start == qsl("startgroup"), startToken); } else if (telegramMeGroup.hasMatch()) { App::joinGroupByHash(telegramMeGroup.captured(1)); } else if (telegramMeStickers.hasMatch()) { @@ -784,7 +784,8 @@ void HashtagLink::onClick(Qt::MouseButton button) const { void BotCommandLink::onClick(Qt::MouseButton button) const { if (button == Qt::LeftButton || button == Qt::MiddleButton) { - App::sendBotCommand(_cmd); + App::insertBotCommand(_cmd); +// App::sendBotCommand(_cmd); } } diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index f4eba9340f..1a2027727d 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -310,6 +310,8 @@ History::History(const PeerId &peerId) : width(0), height(0) , lastWidth(0) , lastScrollTop(History::ScrollMax) , mute(isNotifyMuted(peer->notify)) +, lastKeyboardInited(false) +, lastKeyboardUsed(false) , lastKeyboardId(0) , lastKeyboardFrom(0) , sendRequestId(0) @@ -814,11 +816,21 @@ HistoryItem *History::doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem * } } if (adding->hasReplyMarkup()) { - lastKeyboardId = adding->id; - lastKeyboardFrom = adding->from()->id; - } else if (lastKeyboardFrom == adding->from()->id) { - lastKeyboardId = 0; - lastKeyboardFrom = 0; + if (peer->chat) { + peer->asChat()->markupSenders.insert(adding->from(), true); + } + if (App::replyMarkup(adding->id).flags & MTPDreplyKeyboardMarkup_flag_ZERO) { // zero markup means replyKeyboardHide + if (lastKeyboardFrom == adding->from()->id || (!lastKeyboardInited && !peer->chat && !adding->out())) { + lastKeyboardInited = true; + lastKeyboardId = 0; + lastKeyboardFrom = 0; + } + } else { + lastKeyboardInited = true; + lastKeyboardId = adding->id; + lastKeyboardFrom = adding->from()->id; + lastKeyboardUsed = false; + } } } return adding; @@ -923,12 +935,40 @@ void History::addToFront(const QVector &slice) { } } if (item->from()->id) { - if (lastAuthors && !lastAuthors->contains(item->from())) { - if (item->hasReplyMarkup() && !lastKeyboardId) { + if (lastAuthors) { // chats + if (!lastAuthors->contains(item->from())) { + lastAuthors->push_back(item->from()); + } + if (!lastKeyboardInited && item->hasReplyMarkup() && !item->out()) { // chats with bots + bool wasKeyboardHide = peer->asChat()->markupSenders.contains(item->from()); + if (!wasKeyboardHide) { + peer->asChat()->markupSenders.insert(item->from(), true); + } + if (!(App::replyMarkup(item->id).flags & MTPDreplyKeyboardMarkup_flag_ZERO)) { + if (!lastKeyboardInited) { + lastKeyboardInited = true; + if (wasKeyboardHide) { + lastKeyboardId = 0; + lastKeyboardFrom = 0; + } else { + lastKeyboardId = item->id; + lastKeyboardFrom = item->from()->id; + lastKeyboardUsed = false; + } + } + } + } + } else if (!lastKeyboardInited && item->hasReplyMarkup() && !item->out()) { // conversations with bots + lastKeyboardInited = true; + if (App::replyMarkup(item->id).flags & MTPDreplyKeyboardMarkup_flag_ZERO) { + lastKeyboardId = 0; + lastKeyboardFrom = 0; + } else { + lastKeyboardInited = true; lastKeyboardId = item->id; lastKeyboardFrom = item->from()->id; + lastKeyboardUsed = false; } - lastAuthors->push_back(item->from()); } } } @@ -1286,13 +1326,19 @@ void History::clear(bool leaveItems) { } Parent::clear(); setMsgCount(0); - if (!leaveItems) { + if (leaveItems) { + lastKeyboardInited = false; + } else { setUnreadCount(0); lastMsg = 0; } height = 0; oldLoaded = false; - if (peer->chat) peer->asChat()->lastAuthors.clear(); + if (peer->chat) { + peer->asChat()->lastAuthors.clear(); + peer->asChat()->markupSenders.clear(); + } + if (leaveItems && App::main()) App::main()->historyCleared(this); } History::Parent::iterator History::erase(History::Parent::iterator i) { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 0a0adfeb50..ed92ee7a37 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -251,6 +251,7 @@ struct History : public QList { int32 lastWidth, lastScrollTop; bool mute; + bool lastKeyboardInited, lastKeyboardUsed; MsgId lastKeyboardId; PeerId lastKeyboardFrom; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 2278d35fe0..de579be5e5 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -928,7 +928,7 @@ QString HistoryList::getSelectedText() const { void HistoryList::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { - historyWidget->onClearSelected(); + historyWidget->onListEscapePressed(); } else if (e == QKeySequence::Copy && !_selected.isEmpty()) { copySelectedText(); } else if (e == QKeySequence::Delete) { @@ -1188,6 +1188,9 @@ void HistoryList::onUpdateSelected() { App::hoveredItem(0); } } + if (_dragItem && _dragItem->detached()) { + dragActionCancel(); + } linkTipTimer.start(1000); Qt::CursorShape cur = style::cur_default; @@ -1446,9 +1449,10 @@ void MessageField::focusInEvent(QFocusEvent *e) { emit focused(); } -BotKeyboard::BotKeyboard() : _wasForMsgId(0), +BotKeyboard::BotKeyboard() : _wasForMsgId(0), _height(0), _maxOuterHeight(0), _maximizeSize(false), _singleUse(false), _sel(-1), _down(-1), _hoverAnim(animFunc(this, &BotKeyboard::hoverStep)), _st(&st::botKbButton) { setGeometry(0, 0, _st->margin, _st->margin); + _height = _st->margin; setMouseTracking(true); _cmdTipTimer.setSingleShot(true); @@ -1474,9 +1478,17 @@ void BotKeyboard::paintEvent(QPaintEvent *e) { if (rtl()) rect.moveLeft(width() - rect.left() - rect.width()); + int32 tx = rect.x(), tw = rect.width(); + if (tw > st::botKbFont->elidew + _st->padding * 2) { + tx += _st->padding; + tw -= _st->padding * 2; + } else if (tw > st::botKbFont->elidew) { + tx += (tw - st::botKbFont->elidew) / 2; + tw = st::botKbFont->elidew; + } if (_down == i * MatrixRowShift + j) { App::roundRect(p, rect, st::botKbDownBg, BotKeyboardDownCorners); - btn.text.drawElided(p, rect.x(), rect.y() + _st->downTextTop, rect.width(), 1, style::al_top); + btn.text.drawElided(p, tx, rect.y() + _st->downTextTop + ((rect.height() - _st->height) / 2), tw, 1, style::al_top); } else { App::roundRect(p, rect, st::botKbBg, BotKeyboardCorners); float64 hover = btn.hover; @@ -1485,7 +1497,7 @@ void BotKeyboard::paintEvent(QPaintEvent *e) { App::roundRect(p, rect, st::botKbOverBg, BotKeyboardOverCorners); p.setOpacity(1); } - btn.text.drawElided(p, rect.x(), rect.y() + _st->textTop, rect.width(), 1, style::al_top); + btn.text.drawElided(p, tx, rect.y() + _st->textTop + ((rect.height() - _st->height) / 2), tw, 1, style::al_top); } } if (j < s) break; @@ -1494,13 +1506,15 @@ void BotKeyboard::paintEvent(QPaintEvent *e) { void BotKeyboard::resizeEvent(QResizeEvent *e) { updateStyle(); - int32 h = (_btns.size() + 1) * _st->margin + _btns.size() * _st->height; - if (height() != h) { - resize(width(), h); + + _height = (_btns.size() + 1) * _st->margin + _btns.size() * _st->height; + if (_maximizeSize) _height = qMax(_height, _maxOuterHeight); + if (height() != _height) { + resize(width(), _height); return; } - int32 y = _st->margin; + float64 y = _st->margin, btnh = _btns.isEmpty() ? _st->height : (float64(_height - _st->margin) / _btns.size()); for (int32 i = 0, l = _btns.size(); i != l; ++i) { int32 j = 0, s = _btns.at(i).size(); @@ -1516,15 +1530,15 @@ void BotKeyboard::resizeEvent(QResizeEvent *e) { float64 x = _st->margin, coef = widthForText / widthOfText; for (j = 0; j != s; ++j) { Button &btn(_btns[i][j]); - float64 tw = widthForText / float64(s)/*qMax(btn.text.maxWidth(), 1) * coef*/, w = 2 * _st->padding + tw; + float64 tw = widthForText / float64(s), w = 2 * _st->padding + tw; if (w < _st->padding) w = _st->padding; - btn.rect = QRect(qRound(x), y, qRound(w), _st->height); + btn.rect = QRect(qRound(x), qRound(y), qRound(w), qRound(btnh - _st->margin)); x += w + _st->margin; btn.full = tw >= btn.text.maxWidth(); } - y += _st->height + _st->margin; + y += btnh; } } @@ -1565,11 +1579,15 @@ bool BotKeyboard::updateMarkup(HistoryItem *to) { clearSelection(); _btns.clear(); const ReplyMarkup &markup(App::replyMarkup(to->id)); - if (!markup.isEmpty()) { - int32 i = 0, l = qMin(markup.size(), 32); + _maximizeSize = !(markup.flags & MTPDreplyKeyboardMarkup_flag_resize); + _singleUse = markup.flags & MTPDreplyKeyboardMarkup_flag_single_use; + + const ReplyMarkup::Commands &commands(markup.commands); + if (!commands.isEmpty()) { + int32 i = 0, l = qMin(commands.size(), 32); _btns.reserve(l); for (; i != l; ++i) { - const QList &row(markup.at(i)); + const QList &row(commands.at(i)); QList