Этот коммит содержится в:
John Preston 2016-01-01 23:11:11 +08:00
родитель 259ca7c6cc e88e74ed94
Коммит 8116334557
28 изменённых файлов: 647 добавлений и 341 удалений

Просмотреть файл

@ -1307,6 +1307,13 @@ btnAttachEmoji: iconedButton(btnAttachDocument) {
width: 33px; width: 33px;
} }
emojiCircle: size(19px, 19px);
emojiCirclePeriod: 1500;
emojiCircleDuration: 500;
emojiCircleTop: 13px;
emojiCircleLine: 2px;
emojiCircleFg: #b9b9b9;
emojiCirclePart: 3.5;
btnBotKbShow: iconedButton(btnAttachEmoji) { btnBotKbShow: iconedButton(btnAttachEmoji) {
icon: sprite(375px, 74px, 21px, 21px); icon: sprite(375px, 74px, 21px, 21px);
iconPos: point(6px, 12px); iconPos: point(6px, 12px);

Просмотреть файл

@ -705,10 +705,11 @@ void Application::checkMapVersion() {
if (Local::oldMapVersion() < AppVersion) { if (Local::oldMapVersion() < AppVersion) {
if (Local::oldMapVersion()) { if (Local::oldMapVersion()) {
QString versionFeatures; QString versionFeatures;
if (cDevVersion() && Local::oldMapVersion() < 9014) { if (cDevVersion() && Local::oldMapVersion() < 9016) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Sticker management: manually rearrange your sticker packs, pack order is now synced across all your devices\n\xe2\x80\x94 Click and hold on a sticker to preview it before sending\n\xe2\x80\x94 New context menu for chats in chats list\n\xe2\x80\x94 Support for all existing emoji");// .replace('@', qsl("@") + QChar(0x200D)); // versionFeatures = QString::fromUtf8("\xe2\x80\x94 Sticker management: manually rearrange your sticker packs, pack order is now synced across all your devices\n\xe2\x80\x94 Click and hold on a sticker to preview it before sending\n\xe2\x80\x94 New context menu for chats in chats list\n\xe2\x80\x94 Support for all existing emoji");// .replace('@', qsl("@") + QChar(0x200D));
versionFeatures = lng_new_version_text(lt_gifs_link, qsl("https://telegram.org/blog/gif-revolution"), lt_bots_link, qsl("https://telegram.org/blog/inline-bots")).trimmed();
} else if (Local::oldMapVersion() < 9015) { } else if (Local::oldMapVersion() < 9015) {
// versionFeatures = lang(lng_new_version_text).trimmed(); versionFeatures = lng_new_version_text(lt_gifs_link, qsl("https://telegram.org/blog/gif-revolution"), lt_bots_link, qsl("https://telegram.org/blog/inline-bots")).trimmed();
} else { } else {
versionFeatures = lang(lng_new_version_minor).trimmed(); versionFeatures = lang(lng_new_version_minor).trimmed();
} }

Двоичные данные
Telegram/SourceFiles/art/sprite_200x.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 240 KiB

После

Ширина:  |  Высота:  |  Размер: 238 KiB

Просмотреть файл

@ -23,7 +23,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
static const int32 AppVersion = 9015; static const int32 AppVersion = 9015;
static const wchar_t *AppVersionStr = L"0.9.15"; static const wchar_t *AppVersionStr = L"0.9.15";
static const bool DevVersion = false; static const bool DevVersion = false;
#define BETA_VERSION (9015005ULL) // just comment this line to build public version #define BETA_VERSION (9015006ULL) // just comment this line to build public version
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
static const wchar_t *AppName = L"Telegram Desktop"; static const wchar_t *AppName = L"Telegram Desktop";
@ -87,6 +87,7 @@ enum {
AverageGifSize = 320 * 240, AverageGifSize = 320 * 240,
WaitBeforeGifPause = 200, // wait 200ms for gif draw before pausing it WaitBeforeGifPause = 200, // wait 200ms for gif draw before pausing it
InlineBotRequestDelay = 400, // wait 400ms before context bot realtime request InlineBotRequestDelay = 400, // wait 400ms before context bot realtime request
RecentInlineBotsLimit = 10,
AVBlockSize = 4096, // 4Kb for ffmpeg blocksize AVBlockSize = 4096, // 4Kb for ffmpeg blocksize

Просмотреть файл

@ -872,7 +872,7 @@ void DialogsInner::onHashtagFilterUpdate(QStringRef newFilter) {
} }
_hashtagFilter = newFilter.toString(); _hashtagFilter = newFilter.toString();
if (cRecentSearchHashtags().isEmpty() && cRecentWriteHashtags().isEmpty()) { if (cRecentSearchHashtags().isEmpty() && cRecentWriteHashtags().isEmpty()) {
Local::readRecentHashtags(); Local::readRecentHashtagsAndBots();
} }
const RecentHashtagPack &recent(cRecentSearchHashtags()); const RecentHashtagPack &recent(cRecentSearchHashtags());
_hashtagResults.clear(); _hashtagResults.clear();
@ -1434,7 +1434,7 @@ bool DialogsInner::choosePeer() {
} }
} }
cSetRecentSearchHashtags(recent); cSetRecentSearchHashtags(recent);
Local::writeRecentHashtags(); Local::writeRecentHashtagsAndBots();
emit refreshHashtags(); emit refreshHashtags();
selByMouse = true; selByMouse = true;
@ -1487,7 +1487,7 @@ void DialogsInner::saveRecentHashtags(const QString &text) {
} }
} }
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) { if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
Local::readRecentHashtags(); Local::readRecentHashtagsAndBots();
recent = cRecentSearchHashtags(); recent = cRecentSearchHashtags();
} }
found = true; found = true;
@ -1495,7 +1495,7 @@ void DialogsInner::saveRecentHashtags(const QString &text) {
} }
if (found) { if (found) {
cSetRecentSearchHashtags(recent); cSetRecentSearchHashtags(recent);
Local::writeRecentHashtags(); Local::writeRecentHashtagsAndBots();
} }
} }

Просмотреть файл

@ -1303,7 +1303,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
} }
void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) { void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) {
InlinePaintContext context(getms(), false, _previewShown, false); InlinePaintContext context(getms(), false, Ui::isLayerShown() || Ui::isMediaViewShown() || _previewShown, false);
int32 top = st::emojiPanHeader; int32 top = st::emojiPanHeader;
int32 fromx = rtl() ? (width() - r.x() - r.width()) : r.x(), tox = rtl() ? (width() - r.x()) : (r.x() + r.width()); int32 fromx = rtl() ? (width() - r.x() - r.width()) : r.x(), tox = rtl() ? (width() - r.x()) : (r.x() + r.width());
@ -3560,10 +3560,13 @@ void EmojiPan::inlineBotChanged() {
} }
_inlineCache.clear(); _inlineCache.clear();
s_inner.inlineBotChanged(); s_inner.inlineBotChanged();
Notify::inlineBotRequesting(false);
} }
void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) { void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) {
_inlineRequestId = 0; _inlineRequestId = 0;
Notify::inlineBotRequesting(false);
InlineCache::iterator it = _inlineCache.find(_inlineQuery); InlineCache::iterator it = _inlineCache.find(_inlineQuery);
@ -3659,6 +3662,8 @@ void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) {
bool EmojiPan::inlineResultsFail(const RPCError &error) { bool EmojiPan::inlineResultsFail(const RPCError &error) {
if (mtpIsFlood(error)) return false; if (mtpIsFlood(error)) return false;
Notify::inlineBotRequesting(false);
_inlineRequestId = 0; _inlineRequestId = 0;
return true; return true;
} }
@ -3674,6 +3679,7 @@ void EmojiPan::queryInlineBot(UserData *bot, QString query) {
if (_inlineRequestId) { if (_inlineRequestId) {
MTP::cancel(_inlineRequestId); MTP::cancel(_inlineRequestId);
_inlineRequestId = 0; _inlineRequestId = 0;
Notify::inlineBotRequesting(false);
} }
if (_inlineCache.contains(query)) { if (_inlineCache.contains(query)) {
_inlineRequestTimer.stop(); _inlineRequestTimer.stop();
@ -3696,6 +3702,7 @@ void EmojiPan::onInlineRequest() {
nextOffset = i.value()->nextOffset; nextOffset = i.value()->nextOffset;
if (nextOffset.isEmpty()) return; if (nextOffset.isEmpty()) return;
} }
Notify::inlineBotRequesting(true);
_inlineRequestId = MTP::send(MTPmessages_GetInlineBotResults(_inlineBot->inputUser, MTP_string(_inlineQuery), MTP_string(nextOffset)), rpcDone(&EmojiPan::inlineResultsDone), rpcFail(&EmojiPan::inlineResultsFail)); _inlineRequestId = MTP::send(MTPmessages_GetInlineBotResults(_inlineBot->inputUser, MTP_string(_inlineQuery), MTP_string(nextOffset)), rpcDone(&EmojiPan::inlineResultsDone), rpcFail(&EmojiPan::inlineResultsFail));
} }
@ -3759,7 +3766,7 @@ void MentionsInner::paintEvent(QPaintEvent *e) {
if (selected) { if (selected) {
p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b); p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b);
int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2; int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
if (!_hrows->isEmpty()) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon); if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon);
} }
p.setPen(st::black->p); p.setPen(st::black->p);
if (!_mrows->isEmpty()) { if (!_mrows->isEmpty()) {
@ -3895,6 +3902,10 @@ bool MentionsInner::select() {
return false; return false;
} }
void MentionsInner::setRecentInlineBotsInRows(int32 bots) {
_recentInlineBotsInRows = bots;
}
QString MentionsInner::getSelected() const { QString MentionsInner::getSelected() const {
int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size()); int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size());
if (_sel >= 0 && _sel < maxSel) { if (_sel >= 0 && _sel < maxSel) {
@ -3923,20 +3934,32 @@ void MentionsInner::mousePressEvent(QMouseEvent *e) {
_mouseSel = true; _mouseSel = true;
onUpdateSelected(true); onUpdateSelected(true);
if (e->button() == Qt::LeftButton) { if (e->button() == Qt::LeftButton) {
if (_overDelete && _sel >= 0 && _sel < _hrows->size()) { if (_overDelete && _sel >= 0 && _sel < (_mrows->isEmpty() ? _hrows->size() : _recentInlineBotsInRows)) {
_mousePos = mapToGlobal(e->pos()); _mousePos = mapToGlobal(e->pos());
bool removed = false;
if (_mrows->isEmpty()) {
QString toRemove = _hrows->at(_sel); QString toRemove = _hrows->at(_sel);
RecentHashtagPack recent(cRecentWriteHashtags()); RecentHashtagPack &recent(cRefRecentWriteHashtags());
for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) { for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) {
if (i->first == toRemove) { if (i->first == toRemove) {
i = recent.erase(i); i = recent.erase(i);
removed = true;
} else { } else {
++i; ++i;
} }
} }
cSetRecentWriteHashtags(recent); } else {
Local::writeRecentHashtags(); UserData *toRemove = _mrows->at(_sel);
RecentInlineBots &recent(cRefRecentInlineBots());
int32 index = recent.indexOf(toRemove);
if (index >= 0) {
recent.remove(index);
removed = true;
}
}
if (removed) {
Local::writeRecentHashtagsAndBots();
}
_parent->updateFiltered(); _parent->updateFiltered();
_mouseSel = true; _mouseSel = true;
@ -3973,8 +3996,8 @@ void MentionsInner::onUpdateSelected(bool force) {
if ((!force && !rect().contains(mouse)) || !_mouseSel) return; if ((!force && !rect().contains(mouse)) || !_mouseSel) return;
int w = width(), mouseY = mouse.y(); int w = width(), mouseY = mouse.y();
_overDelete = _mrows->isEmpty() && (mouse.x() >= w - st::mentionHeight);
int32 sel = mouseY / int32(st::mentionHeight), maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size(); int32 sel = mouseY / int32(st::mentionHeight), maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
_overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= w - st::mentionHeight) : false;
if (sel < 0 || sel >= maxSel) { if (sel < 0 || sel >= maxSel) {
sel = -1; sel = -1;
} }
@ -4036,15 +4059,23 @@ void MentionsDropdown::paintEvent(QPaintEvent *e) {
} }
void MentionsDropdown::showFiltered(PeerData *peer, QString start) { void MentionsDropdown::showFiltered(PeerData *peer, QString query, bool start) {
if (query.isEmpty() || (peer->isUser() && query.at(0) == '@' && (!start || cRecentInlineBots().isEmpty()))) {
if (!isHidden()) {
hideStart();
}
return;
}
_chat = peer->asChat(); _chat = peer->asChat();
_user = peer->asUser(); _user = peer->asUser();
_channel = peer->asChannel(); _channel = peer->asChannel();
start = start.toLower(); query = query.toLower();
bool toDown = (_filter != start); bool toDown = (_filter != query);
if (toDown) { if (toDown) {
_filter = start; _filter = query;
} }
_addInlineBots = start;
updateFiltered(toDown); updateFiltered(toDown);
} }
@ -4056,13 +4087,34 @@ bool MentionsDropdown::clearFilteredBotCommands() {
} }
void MentionsDropdown::updateFiltered(bool toDown) { void MentionsDropdown::updateFiltered(bool toDown) {
int32 now = unixtime(); int32 now = unixtime(), recentInlineBots = 0;
MentionRows rows; MentionRows mrows;
HashtagRows hrows; HashtagRows hrows;
BotCommandRows brows; BotCommandRows brows;
if (_filter.at(0) == '@') {
if (_chat) {
mrows.reserve((_addInlineBots ? cRecentInlineBots().size() : 0) + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
} else if (_channel && _channel->isMegagroup()) {
if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
} else {
mrows.reserve((_addInlineBots ? cRecentInlineBots().size() : 0) + _channel->mgInfo->lastParticipants.size());
}
} else if (_addInlineBots) {
mrows.reserve(cRecentInlineBots().size());
}
if (_addInlineBots) {
for (RecentInlineBots::const_iterator i = cRecentInlineBots().cbegin(), e = cRecentInlineBots().cend(); i != e; ++i) {
UserData *user = *i;
if (user->username.isEmpty()) continue;
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
mrows.push_back(user);
++recentInlineBots;
}
}
}
if (_filter.at(0) == '@' && _chat) { if (_filter.at(0) == '@' && _chat) {
QMultiMap<int32, UserData*> ordered; QMultiMap<int32, UserData*> ordered;
rows.reserve(_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()); mrows.reserve(mrows.size() + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
if (_chat->noParticipantInfo()) { if (_chat->noParticipantInfo()) {
if (App::api()) App::api()->requestFullPeer(_chat); if (App::api()) App::api()->requestFullPeer(_chat);
} else if (!_chat->participants.isEmpty()) { } else if (!_chat->participants.isEmpty()) {
@ -4077,7 +4129,7 @@ void MentionsDropdown::updateFiltered(bool toDown) {
UserData *user = *i; UserData *user = *i;
if (user->username.isEmpty()) continue; if (user->username.isEmpty()) continue;
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue; if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
rows.push_back(user); mrows.push_back(user);
if (!ordered.isEmpty()) { if (!ordered.isEmpty()) {
ordered.remove(App::onlineForSort(user, now), user); ordered.remove(App::onlineForSort(user, now), user);
} }
@ -4085,7 +4137,7 @@ void MentionsDropdown::updateFiltered(bool toDown) {
if (!ordered.isEmpty()) { if (!ordered.isEmpty()) {
for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) { for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
--i; --i;
rows.push_back(i.value()); mrows.push_back(i.value());
} }
} }
} else if (_filter.at(0) == '@' && _channel && _channel->isMegagroup()) { } else if (_filter.at(0) == '@' && _channel && _channel->isMegagroup()) {
@ -4093,12 +4145,12 @@ void MentionsDropdown::updateFiltered(bool toDown) {
if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) { if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
if (App::api()) App::api()->requestLastParticipants(_channel); if (App::api()) App::api()->requestLastParticipants(_channel);
} else { } else {
rows.reserve(_channel->mgInfo->lastParticipants.size()); mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size());
for (MegagroupInfo::LastParticipants::const_iterator i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.cend(); i != e; ++i) { for (MegagroupInfo::LastParticipants::const_iterator i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.cend(); i != e; ++i) {
UserData *user = *i; UserData *user = *i;
if (user->username.isEmpty()) continue; if (user->username.isEmpty()) continue;
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue; if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
rows.push_back(user); mrows.push_back(user);
} }
} }
} else if (_filter.at(0) == '#') { } else if (_filter.at(0) == '#') {
@ -4177,7 +4229,8 @@ void MentionsDropdown::updateFiltered(bool toDown) {
} }
} }
} }
rowsUpdated(rows, hrows, brows, toDown); rowsUpdated(mrows, hrows, brows, toDown);
_inner.setRecentInlineBotsInRows(recentInlineBots);
} }
void MentionsDropdown::rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, bool toDown) { void MentionsDropdown::rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, bool toDown) {

Просмотреть файл

@ -736,6 +736,8 @@ public:
bool moveSel(int direction); bool moveSel(int direction);
bool select(); bool select();
void setRecentInlineBotsInRows(int32 bots);
QString getSelected() const; QString getSelected() const;
signals: signals:
@ -756,6 +758,7 @@ private:
MentionRows *_mrows; MentionRows *_mrows;
HashtagRows *_hrows; HashtagRows *_hrows;
BotCommandRows *_brows; BotCommandRows *_brows;
int32 _recentInlineBotsInRows;
int32 _sel; int32 _sel;
bool _mouseSel; bool _mouseSel;
QPoint _mousePos; QPoint _mousePos;
@ -775,7 +778,7 @@ public:
void fastHide(); void fastHide();
bool clearFilteredBotCommands(); bool clearFilteredBotCommands();
void showFiltered(PeerData *peer, QString start); void showFiltered(PeerData *peer, QString query, bool start);
void updateFiltered(bool toDown = false); void updateFiltered(bool toDown = false);
void setBoundings(QRect boundings); void setBoundings(QRect boundings);
@ -820,7 +823,7 @@ private:
HashtagRows _hrows; HashtagRows _hrows;
BotCommandRows _brows; BotCommandRows _brows;
void rowsUpdated(const MentionRows &rows, const HashtagRows &hrows, const BotCommandRows &brows, bool toDown); void rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, bool toDown);
ScrollArea _scroll; ScrollArea _scroll;
MentionsInner _inner; MentionsInner _inner;
@ -830,6 +833,7 @@ private:
ChannelData *_channel; ChannelData *_channel;
QString _filter; QString _filter;
QRect _boundings; QRect _boundings;
bool _addInlineBots;
int32 _width, _height; int32 _width, _height;
bool _hiding; bool _hiding;

Просмотреть файл

@ -129,7 +129,7 @@ namespace Ui {
void showPeerHistoryAsync(const PeerId &peer, MsgId msgId) { void showPeerHistoryAsync(const PeerId &peer, MsgId msgId) {
if (MainWidget *m = App::main()) { if (MainWidget *m = App::main()) {
QMetaObject::invokeMethod(m, SLOT(ui_showPeerHistoryAsync(quint64,qint32)), Qt::QueuedConnection, Q_ARG(quint64, peer), Q_ARG(qint32, msgId)); QMetaObject::invokeMethod(m, "ui_showPeerHistoryAsync", Qt::QueuedConnection, Q_ARG(quint64, peer), Q_ARG(qint32, msgId));
} }
} }
@ -149,6 +149,10 @@ namespace Notify {
if (MainWidget *m = App::main()) m->notify_botCommandsChanged(user); if (MainWidget *m = App::main()) m->notify_botCommandsChanged(user);
} }
void inlineBotRequesting(bool requesting) {
if (MainWidget *m = App::main()) m->notify_inlineBotRequesting(requesting);
}
void migrateUpdated(PeerData *peer) { void migrateUpdated(PeerData *peer) {
if (MainWidget *m = App::main()) m->notify_migrateUpdated(peer); if (MainWidget *m = App::main()) m->notify_migrateUpdated(peer);
} }

Просмотреть файл

@ -80,6 +80,8 @@ namespace Notify {
void userIsContactChanged(UserData *user, bool fromThisApp = false); void userIsContactChanged(UserData *user, bool fromThisApp = false);
void botCommandsChanged(UserData *user); void botCommandsChanged(UserData *user);
void inlineBotRequesting(bool requesting);
void migrateUpdated(PeerData *peer); void migrateUpdated(PeerData *peer);
void clipStopperHidden(ClipStopperType type); void clipStopperHidden(ClipStopperType type);

Просмотреть файл

@ -184,7 +184,7 @@ void AnimationManager::clipCallback(ClipReader *reader, qint32 threadIndex, qint
ClipReader::callback(reader, threadIndex, ClipReaderNotification(notification)); ClipReader::callback(reader, threadIndex, ClipReaderNotification(notification));
} }
QPixmap _prepareFrame(const ClipFrameRequest &request, const QImage &original, QImage &cache, bool hasAlpha) { QPixmap _prepareFrame(const ClipFrameRequest &request, const QImage &original, bool hasAlpha, QImage &cache) {
bool badSize = (original.width() != request.framew) || (original.height() != request.frameh); bool badSize = (original.width() != request.framew) || (original.height() != request.frameh);
bool needOuter = (request.outerw != request.framew) || (request.outerh != request.frameh); bool needOuter = (request.outerw != request.framew) || (request.outerh != request.frameh);
if (badSize || needOuter || hasAlpha || request.rounded) { if (badSize || needOuter || hasAlpha || request.rounded) {
@ -258,14 +258,16 @@ ClipReader::Frame *ClipReader::frameToShow() const { // 0 means not ready
return _frames + (((step + 1) / 2) % 3); return _frames + (((step + 1) / 2) % 3);
} }
ClipReader::Frame *ClipReader::frameToWrite() const { // 0 means not ready ClipReader::Frame *ClipReader::frameToWrite(int32 *index) const { // 0 means not ready
int32 step = _step.loadAcquire(); int32 step = _step.loadAcquire(), i = 0;
if (step == FirstFrameNotReadStep) { if (step == WaitingForRequestStep) {
return _frames; if (index) *index = 0;
} else if (step == WaitingForRequestStep) {
return 0; return 0;
} else if (step != FirstFrameNotReadStep) {
i = (((step + 3) / 2) % 3);
} }
return _frames + (((step + 3) / 2) % 3); if (index) *index = i;
return _frames + i;
} }
ClipReader::Frame *ClipReader::frameToRequestOther(bool check) const { ClipReader::Frame *ClipReader::frameToRequestOther(bool check) const {
@ -342,10 +344,10 @@ QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 oute
frame->request.frameh = frameh * factor; frame->request.frameh = frameh * factor;
frame->request.outerw = outerw * factor; frame->request.outerw = outerw * factor;
frame->request.outerh = outerh * factor; frame->request.outerh = outerh * factor;
frame->pix = QPixmap();
QImage cache; QImage cacheForResize;
frame->pix = _prepareFrame(frame->request, frame->original, cache, true); frame->pix = QPixmap();
frame->pix = _prepareFrame(frame->request, frame->original, true, cacheForResize);
Frame *other = frameToRequestOther(true); Frame *other = frameToRequestOther(true);
if (other) other->request = frame->request; if (other) other->request = frame->request;
@ -830,8 +832,7 @@ public:
, _location(_data.isEmpty() ? new FileLocation(location) : 0) , _location(_data.isEmpty() ? new FileLocation(location) : 0)
, _accessed(false) , _accessed(false)
, _implementation(0) , _implementation(0)
, _currentHasAlpha(true) , _frame(_frames)
, _nextHasAlpha(true)
, _width(0) , _width(0)
, _height(0) , _height(0)
, _previousMs(0) , _previousMs(0)
@ -850,12 +851,12 @@ public:
if (!_implementation && !init()) { if (!_implementation && !init()) {
return error(); return error();
} }
if (_currentOriginal.isNull()) { if (_frame->original.isNull()) {
if (!_implementation->readNextFrame(_currentOriginal, _currentHasAlpha, QSize())) { if (!_implementation->readNextFrame(_frame->original, _frame->alpha, QSize())) {
return error(); return error();
} }
_width = _currentOriginal.width(); _width = _frame->original.width();
_height = _currentOriginal.height(); _height = _frame->original.height();
return ClipProcessReinit; return ClipProcessReinit;
} }
return ClipProcessWait; return ClipProcessWait;
@ -868,12 +869,12 @@ public:
return start(ms); return start(ms);
} }
if (_current.isNull()) { // first frame read, but not yet prepared if (_frame->pix.isNull()) { // first frame read, but not yet prepared
_currentOriginal.setDevicePixelRatio(_request.factor); _frame->original.setDevicePixelRatio(_request.factor);
_previousMs = _currentMs; _previousMs = _currentMs;
_currentMs = ms; _currentMs = ms;
_current = _prepareFrame(_request, _currentOriginal, _currentCache, _currentHasAlpha); _frame->pix = _prepareFrame(_request, _frame->original, _frame->alpha, _frame->cache);
if (!prepareNextFrame()) { if (!prepareNextFrame()) {
return error(); return error();
@ -906,20 +907,16 @@ public:
void swapBuffers(uint64 ms = 0) { void swapBuffers(uint64 ms = 0) {
_previousMs = _currentMs; _previousMs = _currentMs;
_currentMs = qMax(ms, _nextUpdateMs); _currentMs = qMax(ms, _nextUpdateMs);
qSwap(_currentOriginal, _nextOriginal);
qSwap(_current, _next);
qSwap(_currentCache, _nextCache);
qSwap(_currentHasAlpha, _nextHasAlpha);
} }
bool prepareNextFrame() { bool prepareNextFrame() {
if (!_implementation->readNextFrame(_nextOriginal, _nextHasAlpha, QSize(_request.framew, _request.frameh))) { if (!_implementation->readNextFrame(_frame->original, _frame->alpha, QSize(_request.framew, _request.frameh))) {
return false; return false;
} }
_nextUpdateMs = _currentMs + nextFrameDelay(); _nextUpdateMs = _currentMs + nextFrameDelay();
_nextOriginal.setDevicePixelRatio(_request.factor); _frame->original.setDevicePixelRatio(_request.factor);
_next = QPixmap(); _frame->pix = QPixmap();
_next = _prepareFrame(_request, _nextOriginal, _nextCache, _nextHasAlpha); _frame->pix = _prepareFrame(_request, _frame->original, _frame->alpha, _frame->cache);
return true; return true;
} }
@ -979,9 +976,16 @@ private:
ClipReaderImplementation *_implementation; ClipReaderImplementation *_implementation;
ClipFrameRequest _request; ClipFrameRequest _request;
QPixmap _current, _next; struct Frame {
QImage _currentOriginal, _nextOriginal, _currentCache, _nextCache; Frame() : alpha(true) {
bool _currentHasAlpha, _nextHasAlpha; }
QPixmap pix;
QImage original, cache;
bool alpha;
};
Frame _frames[3];
Frame *_frame;
int32 _width, _height; int32 _width, _height;
uint64 _previousMs, _currentMs, _nextUpdateMs; uint64 _previousMs, _currentMs, _nextUpdateMs;
@ -1016,34 +1020,58 @@ void ClipReadManager::start(ClipReader *reader) {
} }
void ClipReadManager::update(ClipReader *reader) { void ClipReadManager::update(ClipReader *reader) {
QMutexLocker lock(&_readerPointersMutex); QReadLocker lock(&_readerPointersMutex);
_readerPointers.insert(reader, reader->_private); ReaderPointers::const_iterator i = _readerPointers.constFind(reader);
if (i == _readerPointers.cend()) {
lock.unlock();
QWriteLocker lock(&_readerPointersMutex);
_readerPointers.insert(reader, MutableAtomicInt(1));
} else {
i->v.storeRelease(1);
}
emit processDelayed(); emit processDelayed();
} }
void ClipReadManager::stop(ClipReader *reader) { void ClipReadManager::stop(ClipReader *reader) {
QMutexLocker lock(&_readerPointersMutex); if (!carries(reader)) return;
QWriteLocker lock(&_readerPointersMutex);
_readerPointers.remove(reader); _readerPointers.remove(reader);
emit processDelayed(); emit processDelayed();
} }
bool ClipReadManager::carries(ClipReader *reader) const { bool ClipReadManager::carries(ClipReader *reader) const {
QMutexLocker lock(&_readerPointersMutex); QReadLocker lock(&_readerPointersMutex);
return _readerPointers.contains(reader); return _readerPointers.contains(reader);
} }
bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) { ClipReadManager::ReaderPointers::iterator ClipReadManager::unsafeFindReaderPointer(ClipReaderPrivate *reader) {
QMutexLocker lock(&_readerPointersMutex);
ReaderPointers::iterator it = _readerPointers.find(reader->_interface); ReaderPointers::iterator it = _readerPointers.find(reader->_interface);
if (it != _readerPointers.cend() && it.key()->_private != reader) {
it = _readerPointers.end(); // it is a new reader which was realloced in the same address // could be a new reader which was realloced in the same address
} return (it == _readerPointers.cend() || it.key()->_private == reader) ? it : _readerPointers.end();
}
ClipReadManager::ReaderPointers::const_iterator ClipReadManager::constUnsafeFindReaderPointer(ClipReaderPrivate *reader) const {
ReaderPointers::const_iterator it = _readerPointers.constFind(reader->_interface);
// could be a new reader which was realloced in the same address
return (it == _readerPointers.cend() || it.key()->_private == reader) ? it : _readerPointers.cend();
}
bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) {
QReadLocker lock(&_readerPointersMutex);
ReaderPointers::const_iterator it = constUnsafeFindReaderPointer(reader);
if (result == ClipProcessError) { if (result == ClipProcessError) {
if (it != _readerPointers.cend()) { if (it != _readerPointers.cend()) {
it.key()->error(); it.key()->error();
emit callback(it.key(), it.key()->threadIndex(), ClipReaderReinit); emit callback(it.key(), it.key()->threadIndex(), ClipReaderReinit);
_readerPointers.erase(it); lock.unlock();
QWriteLocker lock(&_readerPointersMutex);
ReaderPointers::iterator i = unsafeFindReaderPointer(reader);
if (i != _readerPointers.cend()) _readerPointers.erase(i);
} }
return false; return false;
} }
@ -1066,9 +1094,9 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess
if (result == ClipProcessReinit || result == ClipProcessRepaint || result == ClipProcessStarted) { if (result == ClipProcessReinit || result == ClipProcessRepaint || result == ClipProcessStarted) {
ClipReader::Frame *frame = it.key()->frameToWrite(); ClipReader::Frame *frame = it.key()->frameToWrite();
t_assert(frame != 0); t_assert(frame != 0);
frame->pix = QPixmap(); frame->clear();
frame->pix = reader->_current; frame->pix = reader->_frame->pix;
frame->original = reader->_currentOriginal; frame->original = reader->_frame->original;
frame->displayed = false; frame->displayed = false;
it.key()->moveToNextWrite(); it.key()->moveToNextWrite();
if (result == ClipProcessReinit) { if (result == ClipProcessReinit) {
@ -1082,7 +1110,7 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess
ClipReadManager::ResultHandleState ClipReadManager::handleResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) { ClipReadManager::ResultHandleState ClipReadManager::handleResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) {
if (!handleProcessResult(reader, result, ms)) { if (!handleProcessResult(reader, result, ms)) {
_loadLevel.fetchAndAddRelaxed(-1 * (reader->_currentOriginal.isNull() ? AverageGifSize : reader->_width * reader->_height)); _loadLevel.fetchAndAddRelaxed(-1 * (reader->_frame->original.isNull() ? AverageGifSize : reader->_width * reader->_height));
delete reader; delete reader;
return ResultHandleRemove; return ResultHandleRemove;
} }
@ -1110,27 +1138,37 @@ void ClipReadManager::process() {
uint64 ms = getms(), minms = ms + 86400 * 1000ULL; uint64 ms = getms(), minms = ms + 86400 * 1000ULL;
{ {
QMutexLocker lock(&_readerPointersMutex); QReadLocker lock(&_readerPointersMutex);
for (ReaderPointers::iterator i = _readerPointers.begin(), e = _readerPointers.end(); i != e; ++i) { for (ReaderPointers::iterator it = _readerPointers.begin(), e = _readerPointers.end(); it != e; ++it) {
if (i.value()) { if (it->v.loadAcquire()) {
Readers::iterator it = _readers.find(i.value()); Readers::iterator i = _readers.find(it.key()->_private);
if (it == _readers.cend()) { if (i == _readers.cend()) {
_readers.insert(i.value(), 0); _readers.insert(it.key()->_private, 0);
} else { } else {
it.value() = ms; i.value() = ms;
if (it.key()->_paused && !i.key()->_paused.loadAcquire()) { if (i.key()->_paused && !it.key()->_paused.loadAcquire()) {
it.key()->_paused = false; i.key()->_paused = false;
} }
} }
ClipReader::Frame *frame = i.key()->frameToWrite(); ClipReader::Frame *frame = it.key()->frameToWrite();
if (frame) i.value()->_request = frame->request; if (frame) it.key()->_private->_request = frame->request;
i.value() = 0; it->v.storeRelease(0);
} }
} }
} }
for (Readers::iterator i = _readers.begin(), e = _readers.end(); i != e;) { for (Readers::iterator i = _readers.begin(), e = _readers.end(); i != e;) {
if (i.value() <= ms) { if (i.value() <= ms) {
{
QReadLocker lock(&_readerPointersMutex);
ReaderPointers::const_iterator it = constUnsafeFindReaderPointer(i.key());
if (it != _readerPointers.cend()) {
int32 index = 0;
ClipReader::Frame *frame = it.key()->frameToWrite(&index);
if (frame) frame->clear();
i.key()->_frame = i.key()->_frames + index;
}
}
ClipProcessResult result = i.key()->process(ms); ClipProcessResult result = i.key()->process(ms);
ResultHandleState state = handleResult(i.key(), result, ms); ResultHandleState state = handleResult(i.key(), result, ms);
@ -1167,13 +1205,13 @@ void ClipReadManager::finish() {
} }
void ClipReadManager::clear() { void ClipReadManager::clear() {
QMutexLocker lock(&_readerPointersMutex); {
for (ReaderPointers::iterator i = _readerPointers.begin(), e = _readerPointers.end(); i != e; ++i) { QWriteLocker lock(&_readerPointersMutex);
if (i.value()) { for (ReaderPointers::iterator it = _readerPointers.begin(), e = _readerPointers.end(); it != e; ++it) {
i.key()->_private = 0; it.key()->_private = 0;
}
} }
_readerPointers.clear(); _readerPointers.clear();
}
for (Readers::iterator i = _readers.begin(), e = _readers.end(); i != e; ++i) { for (Readers::iterator i = _readers.begin(), e = _readers.end(); i != e; ++i) {
delete i.key(); delete i.key();
@ -1195,12 +1233,12 @@ MTPDocumentAttribute clipReadAnimatedAttributes(const QString &fname, const QByt
if (reader->readNextFrame(cover, hasAlpha, QSize())) { if (reader->readNextFrame(cover, hasAlpha, QSize())) {
if (cover.width() > 0 && cover.height() > 0 && cover.width() < cover.height() * 10 && cover.height() < cover.width() * 10) { if (cover.width() > 0 && cover.height() > 0 && cover.width() < cover.height() * 10 && cover.height() < cover.width() * 10) {
if (hasAlpha) { if (hasAlpha) {
QImage cache; QImage cacheForResize;
ClipFrameRequest request; ClipFrameRequest request;
request.framew = request.outerw = cover.width(); request.framew = request.outerw = cover.width();
request.frameh = request.outerh = cover.height(); request.frameh = request.outerh = cover.height();
request.factor = 1; request.factor = 1;
cover = _prepareFrame(request, cover, cache, hasAlpha).toImage(); cover = _prepareFrame(request, cover, hasAlpha, cacheForResize).toImage();
} }
int32 duration = reader->duration(); int32 duration = reader->duration();
delete reader; delete reader;

Просмотреть файл

@ -563,6 +563,10 @@ private:
struct Frame { struct Frame {
Frame() : displayed(false), when(0) { Frame() : displayed(false), when(0) {
} }
void clear() {
pix = QPixmap();
original = QImage();
}
QPixmap pix; QPixmap pix;
QImage original; QImage original;
ClipFrameRequest request; ClipFrameRequest request;
@ -571,7 +575,7 @@ private:
}; };
mutable Frame _frames[3]; mutable Frame _frames[3];
Frame *frameToShow() const; // 0 means not ready Frame *frameToShow() const; // 0 means not ready
Frame *frameToWrite() const; // 0 means not ready Frame *frameToWrite(int32 *index = 0) const; // 0 means not ready
Frame *frameToRequestOther(bool check) const; Frame *frameToRequestOther(bool check) const;
void moveToNextShow() const; void moveToNextShow() const;
void moveToNextWrite() const; void moveToNextWrite() const;
@ -629,9 +633,17 @@ private:
void clear(); void clear();
QAtomicInt _loadLevel; QAtomicInt _loadLevel;
typedef QMap<ClipReader*, ClipReaderPrivate*> ReaderPointers; struct MutableAtomicInt {
MutableAtomicInt(int value) : v(value) {
}
mutable QAtomicInt v;
};
typedef QMap<ClipReader*, MutableAtomicInt> ReaderPointers;
ReaderPointers _readerPointers; ReaderPointers _readerPointers;
mutable QMutex _readerPointersMutex; mutable QReadWriteLock _readerPointersMutex;
ReaderPointers::const_iterator constUnsafeFindReaderPointer(ClipReaderPrivate *reader) const;
ReaderPointers::iterator unsafeFindReaderPointer(ClipReaderPrivate *reader);
bool handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms); bool handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms);

Просмотреть файл

@ -289,6 +289,59 @@ void MaskedButton::paintEvent(QPaintEvent *e) {
} }
} }
EmojiButton::EmojiButton(QWidget *parent, const style::iconedButton &st) : IconedButton(parent, st)
, _loading(false)
, _a_loading(animation(this, &EmojiButton::step_loading)) {
}
void EmojiButton::paintEvent(QPaintEvent *e) {
QPainter p(this);
uint64 ms = getms();
float64 loading = a_loading.current(ms, _loading ? 1 : 0);
p.setOpacity(_opacity * (1 - loading));
p.fillRect(e->rect(), a_bg.current());
p.setOpacity(a_opacity.current() * _opacity * (1 - loading));
const QRect &i((_state & StateDown) ? _st.downIcon : _st.icon);
if (i.width()) {
const QPoint &t((_state & StateDown) ? _st.downIconPos : _st.iconPos);
p.drawPixmap(t, App::sprite(), i);
}
QRect inner(QPoint((width() - st::emojiCircle.width()) / 2, st::emojiCircleTop), st::emojiCircle);
int32 full = 5760;
int32 start = qRound(full * float64(ms % uint64(st::emojiCirclePeriod)) / st::emojiCirclePeriod), part = qRound(full / st::emojiCirclePart);
p.setBrush(Qt::NoBrush);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.setPen(QPen(st::emojiCircleFg->c, st::emojiCircleLine));
p.setOpacity(a_opacity.current() * _opacity);
p.drawEllipse(inner);
p.setPen(QPen(st::white->c, st::emojiCircleLine));
p.setOpacity(loading);
p.drawArc(inner, (full - start) % full, part);
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
}
void EmojiButton::setLoading(bool loading) {
if (_loading != loading) {
EnsureAnimation(a_loading, _loading ? 1. : 0., func(this, &EmojiButton::update));
a_loading.start(loading ? 1. : 0., st::emojiCircleDuration);
_loading = loading;
if (_loading) {
_a_loading.start();
} else {
_a_loading.stop();
}
}
}
BoxButton::BoxButton(QWidget *parent, const QString &text, const style::BoxButton &st) : Button(parent) BoxButton::BoxButton(QWidget *parent, const QString &text, const style::BoxButton &st) : Button(parent)
, _text(text.toUpper()) , _text(text.toUpper())
, _fullText(text.toUpper()) , _fullText(text.toUpper())

Просмотреть файл

@ -136,6 +136,28 @@ public:
}; };
class EmojiButton : public IconedButton {
Q_OBJECT
public:
EmojiButton(QWidget *parent, const style::iconedButton &st);
void paintEvent(QPaintEvent *e);
void setLoading(bool loading);
private:
bool _loading;
FloatAnimation a_loading;
Animation _a_loading;
void step_loading(uint64 ms, bool timer) {
if (timer) {
update();
}
}
};
class BoxButton : public Button { class BoxButton : public Button {
Q_OBJECT Q_OBJECT

Просмотреть файл

@ -270,7 +270,9 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
return 0; return 0;
} }
void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&inlineBot, QString &inlineBotUsername) const { QString FlatTextarea::getMentionHashtagBotCommandPart(bool &start, UserData *&inlineBot, QString &inlineBotUsername) const {
start = false;
// check inline bot query // check inline bot query
const QString &text(getLastText()); const QString &text(getLastText());
int32 inlineUsernameStart = 1, inlineUsernameLength = 0, size = text.size(); int32 inlineUsernameStart = 1, inlineUsernameLength = 0, size = text.size();
@ -301,13 +303,12 @@ void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&i
inlineBot = InlineBotLookingUpData; inlineBot = InlineBotLookingUpData;
} }
} }
if (inlineBot == InlineBotLookingUpData) return; if (inlineBot == InlineBotLookingUpData) return QString();
if (inlineBot && (!inlineBot->botInfo || inlineBot->botInfo->inlinePlaceholder.isEmpty())) { if (inlineBot && (!inlineBot->botInfo || inlineBot->botInfo->inlinePlaceholder.isEmpty())) {
inlineBot = 0; inlineBot = 0;
} else { } else {
start = text.mid(inlineUsernameStart + inlineUsernameLength + 1); return text.mid(inlineUsernameStart + inlineUsernameLength + 1);
return;
} }
} else { } else {
inlineUsernameLength = 0; inlineUsernameLength = 0;
@ -319,7 +320,7 @@ void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&i
} }
int32 pos = textCursor().position(); int32 pos = textCursor().position();
if (textCursor().anchor() != pos) return; if (textCursor().anchor() != pos) return QString();
// check mention / hashtag / bot command // check mention / hashtag / bot command
QTextDocument *doc(document()); QTextDocument *doc(document());
@ -339,29 +340,33 @@ void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&i
for (int i = pos - p; i > 0; --i) { for (int i = pos - p; i > 0; --i) {
if (t.at(i - 1) == '@') { if (t.at(i - 1) == '@') {
if ((pos - p - i < 1 || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) { if ((pos - p - i < 1 || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
start = t.mid(i - 1, pos - p - i + 1); start = (i == 1) && (p == 0);
return t.mid(i - 1, pos - p - i + 1);
} else if ((pos - p - i < 1 || t.at(i).isLetter()) && i > 2 && (t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_') && !mentionInCommand) { } else if ((pos - p - i < 1 || t.at(i).isLetter()) && i > 2 && (t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_') && !mentionInCommand) {
mentionInCommand = true; mentionInCommand = true;
--i; --i;
continue; continue;
} }
return; return QString();
} else if (t.at(i - 1) == '#') { } else if (t.at(i - 1) == '#') {
if (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_')) { if (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_')) {
start = t.mid(i - 1, pos - p - i + 1); start = (i == 1) && (p == 0);
return t.mid(i - 1, pos - p - i + 1);
} }
return; return QString();
} else if (t.at(i - 1) == '/') { } else if (t.at(i - 1) == '/') {
if (i < 2) { if (i < 2) {
start = t.mid(i - 1, pos - p - i + 1); start = (i == 1) && (p == 0);
return t.mid(i - 1, pos - p - i + 1);
} }
return; return QString();
} }
if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break; if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break;
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break; if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
} }
return; break;
} }
return QString();
} }
void FlatTextarea::onMentionHashtagOrBotCommandInsert(QString str) { void FlatTextarea::onMentionHashtagOrBotCommandInsert(QString str) {

Просмотреть файл

@ -64,7 +64,7 @@ public:
QSize minimumSizeHint() const; QSize minimumSizeHint() const;
EmojiPtr getSingleEmoji() const; EmojiPtr getSingleEmoji() const;
void getMentionHashtagBotCommandStart(QString &start, UserData *&contextBot, QString &contextBotUsername) const; QString getMentionHashtagBotCommandPart(bool &start, UserData *&contextBot, QString &contextBotUsername) const;
void removeSingleEmoji(); void removeSingleEmoji();
bool hasText() const; bool hasText() const;

Просмотреть файл

@ -3028,6 +3028,10 @@ void HistoryWidget::notify_botCommandsChanged(UserData *user) {
} }
} }
void HistoryWidget::notify_inlineBotRequesting(bool requesting) {
_attachEmoji.setLoading(requesting);
}
void HistoryWidget::notify_userIsBotChanged(UserData *user) { void HistoryWidget::notify_userIsBotChanged(UserData *user) {
if (_peer && _peer == user) { if (_peer && _peer == user) {
_list->notifyIsBotChanged(); _list->notifyIsBotChanged();
@ -4971,6 +4975,7 @@ bool HistoryWidget::hasBroadcastToggle() const {
} }
void HistoryWidget::inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result) { void HistoryWidget::inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result) {
Notify::inlineBotRequesting(false);
_inlineBotUsername = QString(); _inlineBotUsername = QString();
if (result.type() == mtpc_contacts_resolvedPeer) { if (result.type() == mtpc_contacts_resolvedPeer) {
const MTPDcontacts_resolvedPeer &d(result.c_contacts_resolvedPeer()); const MTPDcontacts_resolvedPeer &d(result.c_contacts_resolvedPeer());
@ -4982,6 +4987,8 @@ void HistoryWidget::inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result)
bool HistoryWidget::inlineBotResolveFail(QString name, const RPCError &error) { bool HistoryWidget::inlineBotResolveFail(QString name, const RPCError &error) {
if (mtpIsFlood(error)) return false; if (mtpIsFlood(error)) return false;
Notify::inlineBotRequesting(false);
if (name == _inlineBotUsername) { if (name == _inlineBotUsername) {
_inlineBot = 0; _inlineBot = 0;
onCheckMentionDropdown(); onCheckMentionDropdown();
@ -5331,14 +5338,17 @@ void HistoryWidget::onCheckMentionDropdown() {
if (!_history || _a_show.animating()) return; if (!_history || _a_show.animating()) return;
UserData *bot = _inlineBot; UserData *bot = _inlineBot;
QString start, inlineBotUsername(_inlineBotUsername); bool start = false;
_field.getMentionHashtagBotCommandStart(start, _inlineBot, _inlineBotUsername); QString inlineBotUsername(_inlineBotUsername);
QString query = _field.getMentionHashtagBotCommandPart(start, _inlineBot, _inlineBotUsername);
if (inlineBotUsername != _inlineBotUsername) { if (inlineBotUsername != _inlineBotUsername) {
if (_inlineBotResolveRequestId) { if (_inlineBotResolveRequestId) {
Notify::inlineBotRequesting(false);
MTP::cancel(_inlineBotResolveRequestId); MTP::cancel(_inlineBotResolveRequestId);
_inlineBotResolveRequestId = 0; _inlineBotResolveRequestId = 0;
} }
if (_inlineBot == InlineBotLookingUpData) { if (_inlineBot == InlineBotLookingUpData) {
Notify::inlineBotRequesting(true);
_inlineBotResolveRequestId = MTP::send(MTPcontacts_ResolveUsername(MTP_string(_inlineBotUsername)), rpcDone(&HistoryWidget::inlineBotResolveDone), rpcFail(&HistoryWidget::inlineBotResolveFail, _inlineBotUsername)); _inlineBotResolveRequestId = MTP::send(MTPcontacts_ResolveUsername(MTP_string(_inlineBotUsername)), rpcDone(&HistoryWidget::inlineBotResolveDone), rpcFail(&HistoryWidget::inlineBotResolveFail, _inlineBotUsername));
return; return;
} }
@ -5350,10 +5360,10 @@ void HistoryWidget::onCheckMentionDropdown() {
if (_inlineBot != bot) { if (_inlineBot != bot) {
updateFieldPlaceholder(); updateFieldPlaceholder();
} }
if (_inlineBot->username == (cTestMode() ? qstr("contextbot") : qstr("gif")) && start.isEmpty()) { if (_inlineBot->username == (cTestMode() ? qstr("contextbot") : qstr("gif")) && query.isEmpty()) {
_emojiPan.clearInlineBot(); _emojiPan.clearInlineBot();
} else { } else {
_emojiPan.queryInlineBot(_inlineBot, start); _emojiPan.queryInlineBot(_inlineBot, query);
} }
if (!_attachMention.isHidden()) { if (!_attachMention.isHidden()) {
_attachMention.hideStart(); _attachMention.hideStart();
@ -5364,16 +5374,12 @@ void HistoryWidget::onCheckMentionDropdown() {
_field.finishPlaceholder(); _field.finishPlaceholder();
} }
_emojiPan.clearInlineBot(); _emojiPan.clearInlineBot();
if (!start.isEmpty()) { if (!query.isEmpty()) {
if (start.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtags(); if (query.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtagsAndBots();
if (start.at(0) == '@' && _peer->isUser()) return; if (query.at(0) == '@' && cRecentInlineBots().isEmpty()) Local::readRecentHashtagsAndBots();
if (start.at(0) == '/' && _peer->isUser() && !_peer->asUser()->botInfo) return; if (query.at(0) == '/' && _peer->isUser() && !_peer->asUser()->botInfo) return;
_attachMention.showFiltered(_peer, start);
} else {
if (!_attachMention.isHidden()) {
_attachMention.hideStart();
}
} }
_attachMention.showFiltered(_peer, query, start);
} }
} }
@ -6419,6 +6425,18 @@ void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) {
_saveDraftStart = getms(); _saveDraftStart = getms();
onDraftSave(); onDraftSave();
RecentInlineBots &bots(cRefRecentInlineBots());
int32 index = bots.indexOf(bot);
if (index) {
if (index > 0) {
bots.removeAt(index);
} else if (bots.size() >= RecentInlineBotsLimit) {
bots.resize(RecentInlineBotsLimit - 1);
}
bots.push_front(bot);
Local::writeRecentHashtagsAndBots();
}
onCheckMentionDropdown(); onCheckMentionDropdown();
if (!_attachType.isHidden()) _attachType.hideStart(); if (!_attachType.isHidden()) _attachType.hideStart();

Просмотреть файл

@ -568,6 +568,7 @@ public:
void notify_historyItemLayoutChanged(const HistoryItem *item); void notify_historyItemLayoutChanged(const HistoryItem *item);
void notify_automaticLoadSettingsChangedGif(); void notify_automaticLoadSettingsChangedGif();
void notify_botCommandsChanged(UserData *user); void notify_botCommandsChanged(UserData *user);
void notify_inlineBotRequesting(bool requesting);
void notify_userIsBotChanged(UserData *user); void notify_userIsBotChanged(UserData *user);
void notify_migrateUpdated(PeerData *peer); void notify_migrateUpdated(PeerData *peer);
void notify_clipStopperHidden(ClipStopperType type); void notify_clipStopperHidden(ClipStopperType type);
@ -784,7 +785,9 @@ private:
FlatButton _send, _unblock, _botStart, _joinChannel, _muteUnmute; FlatButton _send, _unblock, _botStart, _joinChannel, _muteUnmute;
mtpRequestId _unblockRequest, _reportSpamRequest; mtpRequestId _unblockRequest, _reportSpamRequest;
IconedButton _attachDocument, _attachPhoto, _attachEmoji, _kbShow, _kbHide, _cmdStart; IconedButton _attachDocument, _attachPhoto;
EmojiButton _attachEmoji;
IconedButton _kbShow, _kbHide, _cmdStart;
FlatCheckbox _broadcast; FlatCheckbox _broadcast;
bool _cmdStartShown; bool _cmdStartShown;
MessageField _field; MessageField _field;

Просмотреть файл

@ -1529,13 +1529,14 @@ LayoutInlineGif::~LayoutInlineGif() {
} }
void LayoutInlineGif::prepareThumb(int32 width, int32 height, const QSize &frame) const { void LayoutInlineGif::prepareThumb(int32 width, int32 height, const QSize &frame) const {
if (_doc && !_doc->thumb->isNull()) { DocumentData *doc = _doc ? _doc : (_result ? _result->doc : 0);
if (_doc->thumb->loaded()) { if (doc && !doc->thumb->isNull()) {
if (doc->thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = _doc->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), true, false, false, width, height); _thumb = doc->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), true, false, false, width, height);
} }
} else { } else {
_doc->thumb->load(); doc->thumb->load();
} }
} else if (_result && !_result->thumb_url.isEmpty()) { } else if (_result && !_result->thumb_url.isEmpty()) {
if (_result->thumb->loaded()) { if (_result->thumb->loaded()) {
@ -1746,19 +1747,20 @@ QSize LayoutInlinePhoto::countFrameSize() const {
} }
void LayoutInlinePhoto::prepareThumb(int32 width, int32 height, const QSize &frame) const { void LayoutInlinePhoto::prepareThumb(int32 width, int32 height, const QSize &frame) const {
if (_photo) { PhotoData *photo = _photo ? _photo : (_result ? _result->photo : 0);
if (_photo->medium->loaded()) { if (photo) {
if (photo->medium->loaded()) {
if (!_thumbLoaded || _thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { if (!_thumbLoaded || _thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = _photo->medium->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), true, false, false, width, height); _thumb = photo->medium->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), true, false, false, width, height);
} }
_thumbLoaded = true; _thumbLoaded = true;
} else { } else {
if (_photo->thumb->loaded()) { if (photo->thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = _photo->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), true, false, false, width, height); _thumb = photo->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), true, false, false, width, height);
} }
} }
_photo->medium->load(); photo->medium->load();
} }
} else { } else {
if (_result->thumb->loaded()) { if (_result->thumb->loaded()) {

Просмотреть файл

@ -548,12 +548,12 @@ namespace {
lskRecentStickersOld = 0x07, // no data lskRecentStickersOld = 0x07, // no data
lskBackground = 0x08, // no data lskBackground = 0x08, // no data
lskUserSettings = 0x09, // no data lskUserSettings = 0x09, // no data
lskRecentHashtags = 0x0a, // no data lskRecentHashtagsAndBots = 0x0a, // no data
lskStickers = 0x0b, // no data lskStickers = 0x0b, // no data
lskSavedPeers = 0x0c, // no data lskSavedPeers = 0x0c, // no data
lskReportSpamStatuses = 0x0d, // no data lskReportSpamStatuses = 0x0d, // no data
lskSavedGifsOld = 0x0e, lskSavedGifsOld = 0x0e, // no data
lskSavedGifs = 0x0f, lskSavedGifs = 0x0f, // no data
}; };
typedef QMap<PeerId, FileKey> DraftsMap; typedef QMap<PeerId, FileKey> DraftsMap;
@ -581,8 +581,8 @@ namespace {
bool _backgroundWasRead = false; bool _backgroundWasRead = false;
FileKey _userSettingsKey = 0; FileKey _userSettingsKey = 0;
FileKey _recentHashtagsKey = 0; FileKey _recentHashtagsAndBotsKey = 0;
bool _recentHashtagsWereRead = false; bool _recentHashtagsAndBotsWereRead = false;
FileKey _savedPeersKey = 0; FileKey _savedPeersKey = 0;
@ -726,6 +726,7 @@ namespace {
_fileLocationAliases.insert(MediaKey(kfirst, ksecond), MediaKey(vfirst, vsecond)); _fileLocationAliases.insert(MediaKey(kfirst, ksecond), MediaKey(vfirst, vsecond));
} }
if (!locations.stream.atEnd()) {
_storageWebFilesSize = 0; _storageWebFilesSize = 0;
_webFilesMap.clear(); _webFilesMap.clear();
@ -741,6 +742,7 @@ namespace {
} }
} }
} }
}
void _writeReportSpamStatuses() { void _writeReportSpamStatuses() {
if (!_working()) return; if (!_working()) return;
@ -1667,7 +1669,7 @@ namespace {
qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0; qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0;
quint64 locationsKey = 0, reportSpamStatusesKey = 0; quint64 locationsKey = 0, reportSpamStatusesKey = 0;
quint64 recentStickersKeyOld = 0, stickersKey = 0, savedGifsKey = 0; quint64 recentStickersKeyOld = 0, stickersKey = 0, savedGifsKey = 0;
quint64 backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0, savedPeersKey = 0; quint64 backgroundKey = 0, userSettingsKey = 0, recentHashtagsAndBotsKey = 0, savedPeersKey = 0;
while (!map.stream.atEnd()) { while (!map.stream.atEnd()) {
quint32 keyType; quint32 keyType;
map.stream >> keyType; map.stream >> keyType;
@ -1744,8 +1746,8 @@ namespace {
case lskUserSettings: { case lskUserSettings: {
map.stream >> userSettingsKey; map.stream >> userSettingsKey;
} break; } break;
case lskRecentHashtags: { case lskRecentHashtagsAndBots: {
map.stream >> recentHashtagsKey; map.stream >> recentHashtagsAndBotsKey;
} break; } break;
case lskStickers: { case lskStickers: {
map.stream >> stickersKey; map.stream >> stickersKey;
@ -1788,7 +1790,7 @@ namespace {
_savedPeersKey = savedPeersKey; _savedPeersKey = savedPeersKey;
_backgroundKey = backgroundKey; _backgroundKey = backgroundKey;
_userSettingsKey = userSettingsKey; _userSettingsKey = userSettingsKey;
_recentHashtagsKey = recentHashtagsKey; _recentHashtagsAndBotsKey = recentHashtagsAndBotsKey;
_oldMapVersion = mapData.version; _oldMapVersion = mapData.version;
if (_oldMapVersion < AppVersion) { if (_oldMapVersion < AppVersion) {
_mapChanged = true; _mapChanged = true;
@ -1861,7 +1863,7 @@ namespace {
if (_savedPeersKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_savedPeersKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_recentHashtagsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_recentHashtagsAndBotsKey) mapSize += sizeof(quint32) + sizeof(quint64);
EncryptedDescriptor mapData(mapSize); EncryptedDescriptor mapData(mapSize);
if (!_draftsMap.isEmpty()) { if (!_draftsMap.isEmpty()) {
mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size()); mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size());
@ -1917,8 +1919,8 @@ namespace {
if (_userSettingsKey) { if (_userSettingsKey) {
mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey); mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey);
} }
if (_recentHashtagsKey) { if (_recentHashtagsAndBotsKey) {
mapData.stream << quint32(lskRecentHashtags) << quint64(_recentHashtagsKey); mapData.stream << quint32(lskRecentHashtagsAndBots) << quint64(_recentHashtagsAndBotsKey);
} }
map.writeEncrypted(mapData); map.writeEncrypted(mapData);
@ -2187,7 +2189,7 @@ namespace Local {
_storageWebFilesSize = 0; _storageWebFilesSize = 0;
_locationsKey = _reportSpamStatusesKey = 0; _locationsKey = _reportSpamStatusesKey = 0;
_recentStickersKeyOld = _stickersKey = _savedGifsKey = 0; _recentStickersKeyOld = _stickersKey = _savedGifsKey = 0;
_backgroundKey = _userSettingsKey = _recentHashtagsKey = _savedPeersKey = 0; _backgroundKey = _userSettingsKey = _recentHashtagsAndBotsKey = _savedPeersKey = 0;
_oldMapVersion = _oldSettingsVersion = 0; _oldMapVersion = _oldSettingsVersion = 0;
_mapChanged = true; _mapChanged = true;
_writeMap(WriteMapNow); _writeMap(WriteMapNow);
@ -3258,89 +3260,6 @@ namespace Local {
return false; return false;
} }
void writeRecentHashtags() {
if (!_working()) return;
const RecentHashtagPack &write(cRecentWriteHashtags()), &search(cRecentSearchHashtags());
if (write.isEmpty() && search.isEmpty()) readRecentHashtags();
if (write.isEmpty() && search.isEmpty()) {
if (_recentHashtagsKey) {
clearKey(_recentHashtagsKey);
_recentHashtagsKey = 0;
_mapChanged = true;
}
_writeMap();
} else {
if (!_recentHashtagsKey) {
_recentHashtagsKey = genKey();
_mapChanged = true;
_writeMap(WriteMapFast);
}
quint32 size = sizeof(quint32) * 2, writeCnt = 0, searchCnt = 0;
for (RecentHashtagPack::const_iterator i = write.cbegin(); i != write.cend(); ++i) {
if (!i->first.isEmpty()) {
size += _stringSize(i->first) + sizeof(quint16);
++writeCnt;
}
}
for (RecentHashtagPack::const_iterator i = search.cbegin(); i != search.cend(); ++i) {
if (!i->first.isEmpty()) {
size += _stringSize(i->first) + sizeof(quint16);
++searchCnt;
}
}
EncryptedDescriptor data(size);
data.stream << quint32(writeCnt) << quint32(searchCnt);
for (RecentHashtagPack::const_iterator i = write.cbegin(); i != write.cend(); ++i) {
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
}
for (RecentHashtagPack::const_iterator i = search.cbegin(); i != search.cend(); ++i) {
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
}
FileWriteDescriptor file(_recentHashtagsKey);
file.writeEncrypted(data);
}
}
void readRecentHashtags() {
if (_recentHashtagsWereRead) return;
_recentHashtagsWereRead = true;
if (!_recentHashtagsKey) return;
FileReadDescriptor hashtags;
if (!readEncryptedFile(hashtags, _recentHashtagsKey)) {
clearKey(_recentHashtagsKey);
_recentHashtagsKey = 0;
_writeMap();
return;
}
quint32 writeCount = 0, searchCount = 0;
hashtags.stream >> writeCount >> searchCount;
QString tag;
quint16 count;
RecentHashtagPack write, search;
if (writeCount) {
write.reserve(writeCount);
for (uint32 i = 0; i < writeCount; ++i) {
hashtags.stream >> tag >> count;
write.push_back(qMakePair(tag.trimmed(), count));
}
}
if (searchCount) {
search.reserve(searchCount);
for (uint32 i = 0; i < searchCount; ++i) {
hashtags.stream >> tag >> count;
search.push_back(qMakePair(tag.trimmed(), count));
}
}
cSetRecentWriteHashtags(write);
cSetRecentSearchHashtags(search);
}
uint32 _peerSize(PeerData *peer) { uint32 _peerSize(PeerData *peer) {
uint32 result = sizeof(quint64) + sizeof(quint64) + _storageImageLocationSize(); uint32 result = sizeof(quint64) + sizeof(quint64) + _storageImageLocationSize();
if (peer->isUser()) { if (peer->isUser()) {
@ -3370,7 +3289,7 @@ namespace Local {
return result; return result;
} }
void _writePeer(QDataStream &stream, PeerData *peer) { void _writePeer(QDataStream &stream, PeerData *peer, int32 fileVersion = AppVersion) {
stream << quint64(peer->id) << quint64(peer->photoId); stream << quint64(peer->id) << quint64(peer->photoId);
_writeStorageImageLocation(stream, peer->photoLoc); _writeStorageImageLocation(stream, peer->photoLoc);
if (peer->isUser()) { if (peer->isUser()) {
@ -3380,6 +3299,9 @@ namespace Local {
if (AppVersion >= 9012) { if (AppVersion >= 9012) {
stream << qint32(user->flags); stream << qint32(user->flags);
} }
if (AppVersion >= 9016 || fileVersion >= 9016) {
stream << (user->botInfo ? user->botInfo->inlinePlaceholder : QString());
}
stream << qint32(user->onlineTill) << qint32(user->contact) << qint32(user->botInfo ? user->botInfo->version : -1); stream << qint32(user->onlineTill) << qint32(user->contact) << qint32(user->botInfo ? user->botInfo->version : -1);
} else if (peer->isChat()) { } else if (peer->isChat()) {
ChatData *chat = peer->asChat(); ChatData *chat = peer->asChat();
@ -3396,30 +3318,39 @@ namespace Local {
} }
} }
PeerData *_readPeer(FileReadDescriptor &from) { PeerData *_readPeer(FileReadDescriptor &from, int32 fileVersion = 0) {
PeerData *result = 0; PeerData *result = 0;
quint64 peerId = 0, photoId = 0; quint64 peerId = 0, photoId = 0;
from.stream >> peerId >> photoId; from.stream >> peerId >> photoId;
StorageImageLocation photoLoc(_readStorageImageLocation(from)); StorageImageLocation photoLoc(_readStorageImageLocation(from));
result = App::peerLoaded(peerId);
bool wasLoaded = (result && result->loaded);
if (!wasLoaded) {
result = App::peer(peerId); result = App::peer(peerId);
result->loaded = true; result->loaded = true;
}
if (result->isUser()) { if (result->isUser()) {
UserData *user = result->asUser(); UserData *user = result->asUser();
QString first, last, phone, username; QString first, last, phone, username, inlinePlaceholder;
quint64 access; quint64 access;
qint32 flags = 0, onlineTill, contact, botInfoVersion; qint32 flags = 0, onlineTill, contact, botInfoVersion;
from.stream >> first >> last >> phone >> username >> access; from.stream >> first >> last >> phone >> username >> access;
if (from.version >= 9012) { if (from.version >= 9012) {
from.stream >> flags; from.stream >> flags;
} }
if (from.version >= 9016 || fileVersion >= 9016) {
from.stream >> inlinePlaceholder;
}
from.stream >> onlineTill >> contact >> botInfoVersion; from.stream >> onlineTill >> contact >> botInfoVersion;
bool showPhone = !isServiceUser(user->id) && (peerToUser(user->id) != MTP::authedId()) && (contact <= 0); bool showPhone = !isServiceUser(user->id) && (peerToUser(user->id) != MTP::authedId()) && (contact <= 0);
QString pname = (showPhone && !phone.isEmpty()) ? App::formatPhone(phone) : QString(); QString pname = (showPhone && !phone.isEmpty()) ? App::formatPhone(phone) : QString();
if (!wasLoaded) {
user->setName(first, last, pname, username); user->setName(first, last, pname, username);
user->access = access; user->access = access;
@ -3427,6 +3358,9 @@ namespace Local {
user->onlineTill = onlineTill; user->onlineTill = onlineTill;
user->contact = contact; user->contact = contact;
user->setBotInfoVersion(botInfoVersion); user->setBotInfoVersion(botInfoVersion);
if (!inlinePlaceholder.isEmpty() && user->botInfo) {
user->botInfo->inlinePlaceholder = inlinePlaceholder;
}
if (peerToUser(user->id) == MTP::authedId()) { if (peerToUser(user->id) == MTP::authedId()) {
user->input = MTP_inputPeerSelf(); user->input = MTP_inputPeerSelf();
@ -3437,6 +3371,7 @@ namespace Local {
} }
user->photo = photoLoc.isNull() ? ImagePtr(userDefPhoto(user->colorIndex)) : ImagePtr(photoLoc); user->photo = photoLoc.isNull() ? ImagePtr(userDefPhoto(user->colorIndex)) : ImagePtr(photoLoc);
}
} else if (result->isChat()) { } else if (result->isChat()) {
ChatData *chat = result->asChat(); ChatData *chat = result->asChat();
@ -3450,6 +3385,7 @@ namespace Local {
// flagsData was haveLeft // flagsData was haveLeft
flags = (flagsData == 1 ? MTPDchat::flag_left : 0); flags = (flagsData == 1 ? MTPDchat::flag_left : 0);
} }
if (!wasLoaded) {
chat->updateName(name, QString(), QString()); chat->updateName(name, QString(), QString());
chat->count = count; chat->count = count;
chat->date = date; chat->date = date;
@ -3463,6 +3399,7 @@ namespace Local {
chat->inputChat = MTP_int(peerToChat(chat->id)); chat->inputChat = MTP_int(peerToChat(chat->id));
chat->photo = photoLoc.isNull() ? ImagePtr(chatDefPhoto(chat->colorIndex)) : ImagePtr(photoLoc); chat->photo = photoLoc.isNull() ? ImagePtr(chatDefPhoto(chat->colorIndex)) : ImagePtr(photoLoc);
}
} else if (result->isChannel()) { } else if (result->isChannel()) {
ChannelData *channel = result->asChannel(); ChannelData *channel = result->asChannel();
@ -3471,6 +3408,7 @@ namespace Local {
qint32 date, version, adminned, forbidden, flags; qint32 date, version, adminned, forbidden, flags;
from.stream >> name >> access >> date >> version >> forbidden >> flags >> invitationUrl; from.stream >> name >> access >> date >> version >> forbidden >> flags >> invitationUrl;
if (!wasLoaded) {
channel->updateName(name, QString(), QString()); channel->updateName(name, QString(), QString());
channel->access = access; channel->access = access;
channel->date = date; channel->date = date;
@ -3484,11 +3422,121 @@ namespace Local {
channel->photo = photoLoc.isNull() ? ImagePtr((channel->isMegagroup() ? chatDefPhoto(channel->colorIndex) : channelDefPhoto(channel->colorIndex))) : ImagePtr(photoLoc); channel->photo = photoLoc.isNull() ? ImagePtr((channel->isMegagroup() ? chatDefPhoto(channel->colorIndex) : channelDefPhoto(channel->colorIndex))) : ImagePtr(photoLoc);
} }
}
if (!wasLoaded) {
App::markPeerUpdated(result); App::markPeerUpdated(result);
emit App::main()->peerPhotoChanged(result); emit App::main()->peerPhotoChanged(result);
}
return result; return result;
} }
void writeRecentHashtagsAndBots() {
if (!_working()) return;
const RecentHashtagPack &write(cRecentWriteHashtags()), &search(cRecentSearchHashtags());
const RecentInlineBots &bots(cRecentInlineBots());
if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) readRecentHashtagsAndBots();
if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) {
if (_recentHashtagsAndBotsKey) {
clearKey(_recentHashtagsAndBotsKey);
_recentHashtagsAndBotsKey = 0;
_mapChanged = true;
}
_writeMap();
} else {
if (!_recentHashtagsAndBotsKey) {
_recentHashtagsAndBotsKey = genKey();
_mapChanged = true;
_writeMap(WriteMapFast);
}
quint32 size = sizeof(quint32) * 3, writeCnt = 0, searchCnt = 0, botsCnt = cRecentInlineBots().size();
for (RecentHashtagPack::const_iterator i = write.cbegin(), e = write.cend(); i != e; ++i) {
if (!i->first.isEmpty()) {
size += _stringSize(i->first) + sizeof(quint16);
++writeCnt;
}
}
for (RecentHashtagPack::const_iterator i = search.cbegin(), e = search.cend(); i != e; ++i) {
if (!i->first.isEmpty()) {
size += _stringSize(i->first) + sizeof(quint16);
++searchCnt;
}
}
for (RecentInlineBots::const_iterator i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
size += _peerSize(*i);
}
EncryptedDescriptor data(size);
data.stream << quint32(writeCnt) << quint32(searchCnt);
for (RecentHashtagPack::const_iterator i = write.cbegin(), e = write.cend(); i != e; ++i) {
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
}
for (RecentHashtagPack::const_iterator i = search.cbegin(), e = search.cend(); i != e; ++i) {
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
}
data.stream << quint32(botsCnt);
for (RecentInlineBots::const_iterator i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
_writePeer(data.stream, *i, 9016);
}
FileWriteDescriptor file(_recentHashtagsAndBotsKey);
file.writeEncrypted(data);
}
}
void readRecentHashtagsAndBots() {
if (_recentHashtagsAndBotsWereRead) return;
_recentHashtagsAndBotsWereRead = true;
if (!_recentHashtagsAndBotsKey) return;
FileReadDescriptor hashtags;
if (!readEncryptedFile(hashtags, _recentHashtagsAndBotsKey)) {
clearKey(_recentHashtagsAndBotsKey);
_recentHashtagsAndBotsKey = 0;
_writeMap();
return;
}
quint32 writeCount = 0, searchCount = 0, botsCount = 0;
hashtags.stream >> writeCount >> searchCount;
QString tag;
quint16 count;
RecentHashtagPack write, search;
RecentInlineBots bots;
if (writeCount) {
write.reserve(writeCount);
for (uint32 i = 0; i < writeCount; ++i) {
hashtags.stream >> tag >> count;
write.push_back(qMakePair(tag.trimmed(), count));
}
}
if (searchCount) {
search.reserve(searchCount);
for (uint32 i = 0; i < searchCount; ++i) {
hashtags.stream >> tag >> count;
search.push_back(qMakePair(tag.trimmed(), count));
}
}
cSetRecentWriteHashtags(write);
cSetRecentSearchHashtags(search);
if (!hashtags.stream.atEnd()) {
hashtags.stream >> botsCount;
if (botsCount) {
bots.reserve(botsCount);
for (uint32 i = 0; i < botsCount; ++i) {
PeerData *peer = _readPeer(hashtags, 9016);
if (peer && peer->isUser() && peer->asUser()->botInfo && !peer->asUser()->botInfo->inlinePlaceholder.isEmpty() && !peer->asUser()->username.isEmpty()) {
bots.push_back(peer->asUser());
}
}
}
cSetRecentInlineBots(bots);
}
}
void writeSavedPeers() { void writeSavedPeers() {
if (!_working()) return; if (!_working()) return;
@ -3594,6 +3642,7 @@ namespace Local {
struct ClearManagerData { struct ClearManagerData {
QThread *thread; QThread *thread;
StorageMap images, stickers, audios; StorageMap images, stickers, audios;
WebFilesMap webFiles;
QMutex mutex; QMutex mutex;
QList<int> tasks; QList<int> tasks;
bool working; bool working;
@ -3650,8 +3699,8 @@ namespace Local {
_stickersKey = 0; _stickersKey = 0;
_mapChanged = true; _mapChanged = true;
} }
if (_recentHashtagsKey) { if (_recentHashtagsAndBotsKey) {
_recentHashtagsKey = 0; _recentHashtagsAndBotsKey = 0;
_mapChanged = true; _mapChanged = true;
} }
if (_savedPeersKey) { if (_savedPeersKey) {
@ -3693,6 +3742,22 @@ namespace Local {
_storageStickersSize = 0; _storageStickersSize = 0;
_mapChanged = true; _mapChanged = true;
} }
if (data->webFiles.isEmpty()) {
data->webFiles = _webFilesMap;
} else {
for (WebFilesMap::const_iterator i = _webFilesMap.cbegin(), e = _webFilesMap.cend(); i != e; ++i) {
QString k = i.key();
while (data->webFiles.constFind(k) != data->webFiles.cend()) {
k += '#';
}
data->webFiles.insert(k, i.value());
}
}
if (!_webFilesMap.isEmpty()) {
_webFilesMap.clear();
_storageWebFilesSize = 0;
_writeLocations();
}
if (data->audios.isEmpty()) { if (data->audios.isEmpty()) {
data->audios = _audiosMap; data->audios = _audiosMap;
} else { } else {
@ -3745,6 +3810,7 @@ namespace Local {
int task = 0; int task = 0;
bool result = false; bool result = false;
StorageMap images, stickers, audios; StorageMap images, stickers, audios;
WebFilesMap webFiles;
{ {
QMutexLocker lock(&data->mutex); QMutexLocker lock(&data->mutex);
if (data->tasks.isEmpty()) { if (data->tasks.isEmpty()) {
@ -3755,6 +3821,7 @@ namespace Local {
images = data->images; images = data->images;
stickers = data->stickers; stickers = data->stickers;
audios = data->audios; audios = data->audios;
webFiles = data->webFiles;
} }
switch (task) { switch (task) {
case ClearManagerAll: { case ClearManagerAll: {
@ -3786,6 +3853,9 @@ namespace Local {
for (StorageMap::const_iterator i = audios.cbegin(), e = audios.cend(); i != e; ++i) { for (StorageMap::const_iterator i = audios.cbegin(), e = audios.cend(); i != e; ++i) {
clearKey(i.value().first, UserPath); clearKey(i.value().first, UserPath);
} }
for (WebFilesMap::const_iterator i = webFiles.cbegin(), e = webFiles.cend(); i != e; ++i) {
clearKey(i.value().first, UserPath);
}
result = true; result = true;
break; break;
} }

Просмотреть файл

@ -157,8 +157,8 @@ namespace Local {
void writeBackground(int32 id, const QImage &img); void writeBackground(int32 id, const QImage &img);
bool readBackground(); bool readBackground();
void writeRecentHashtags(); void writeRecentHashtagsAndBots();
void readRecentHashtags(); void readRecentHashtagsAndBots();
void addSavedPeer(PeerData *peer, const QDateTime &position); void addSavedPeer(PeerData *peer, const QDateTime &position);
void removeSavedPeer(PeerData *peer); void removeSavedPeer(PeerData *peer);

Просмотреть файл

@ -759,6 +759,10 @@ void MainWidget::notify_botCommandsChanged(UserData *bot) {
history.notify_botCommandsChanged(bot); history.notify_botCommandsChanged(bot);
} }
void MainWidget::notify_inlineBotRequesting(bool requesting) {
history.notify_inlineBotRequesting(requesting);
}
void MainWidget::notify_userIsBotChanged(UserData *bot) { void MainWidget::notify_userIsBotChanged(UserData *bot) {
history.notify_userIsBotChanged(bot); history.notify_userIsBotChanged(bot);
} }
@ -1345,7 +1349,7 @@ void MainWidget::saveRecentHashtags(const QString &text) {
} }
} }
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) { if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
Local::readRecentHashtags(); Local::readRecentHashtagsAndBots();
recent = cRecentWriteHashtags(); recent = cRecentWriteHashtags();
} }
found = true; found = true;
@ -1353,7 +1357,7 @@ void MainWidget::saveRecentHashtags(const QString &text) {
} }
if (found) { if (found) {
cSetRecentWriteHashtags(recent); cSetRecentWriteHashtags(recent);
Local::writeRecentHashtags(); Local::writeRecentHashtagsAndBots();
} }
} }

Просмотреть файл

@ -416,6 +416,7 @@ public:
void ui_showPeerHistory(quint64 peer, qint32 msgId, bool back); void ui_showPeerHistory(quint64 peer, qint32 msgId, bool back);
void notify_botCommandsChanged(UserData *bot); void notify_botCommandsChanged(UserData *bot);
void notify_inlineBotRequesting(bool requesting);
void notify_userIsBotChanged(UserData *bot); void notify_userIsBotChanged(UserData *bot);
void notify_userIsContactChanged(UserData *user, bool fromThisApp); void notify_userIsContactChanged(UserData *user, bool fromThisApp);
void notify_migrateUpdated(PeerData *peer); void notify_migrateUpdated(PeerData *peer);

Просмотреть файл

@ -121,6 +121,8 @@ int32 gSavedGifsLimit = 100;
RecentHashtagPack gRecentWriteHashtags, gRecentSearchHashtags; RecentHashtagPack gRecentWriteHashtags, gRecentSearchHashtags;
RecentInlineBots gRecentInlineBots;
bool gPasswordRecovered = false; bool gPasswordRecovered = false;
int32 gPasscodeBadTries = 0; int32 gPasscodeBadTries = 0;
uint64 gPasscodeLastTry = 0; uint64 gPasscodeLastTry = 0;

Просмотреть файл

@ -230,9 +230,13 @@ DeclareSetting(bool, ShowingSavedGifs);
DeclareSetting(int32, SavedGifsLimit); DeclareSetting(int32, SavedGifsLimit);
typedef QList<QPair<QString, ushort> > RecentHashtagPack; typedef QList<QPair<QString, ushort> > RecentHashtagPack;
DeclareSetting(RecentHashtagPack, RecentWriteHashtags); DeclareRefSetting(RecentHashtagPack, RecentWriteHashtags);
DeclareSetting(RecentHashtagPack, RecentSearchHashtags); DeclareSetting(RecentHashtagPack, RecentSearchHashtags);
class UserData;
typedef QVector<UserData*> RecentInlineBots;
DeclareRefSetting(RecentInlineBots, RecentInlineBots);
DeclareSetting(bool, PasswordRecovered); DeclareSetting(bool, PasswordRecovered);
DeclareSetting(int32, PasscodeBadTries); DeclareSetting(int32, PasscodeBadTries);

Просмотреть файл

@ -575,7 +575,7 @@ void SettingsInner::paintEvent(QPaintEvent *e) {
top += st::setHeaderSkip; top += st::setHeaderSkip;
int32 cntImages = Local::hasImages() + Local::hasStickers(), cntAudios = Local::hasAudios(); int32 cntImages = Local::hasImages() + Local::hasStickers() + Local::hasWebFiles(), cntAudios = Local::hasAudios();
if (cntImages > 0 && cntAudios > 0) { if (cntImages > 0 && cntAudios > 0) {
if (_localStorageHeight != 2) { if (_localStorageHeight != 2) {
cntAudios = 0; cntAudios = 0;
@ -587,7 +587,7 @@ void SettingsInner::paintEvent(QPaintEvent *e) {
} }
} }
if (cntImages > 0) { if (cntImages > 0) {
QString cnt = lng_settings_images_cached(lt_count, cntImages, lt_size, formatSizeText(Local::storageImagesSize() + Local::storageStickersSize())); QString cnt = lng_settings_images_cached(lt_count, cntImages, lt_size, formatSizeText(Local::storageImagesSize() + Local::storageStickersSize() + Local::storageWebFilesSize()));
p.drawText(_left + st::setHeaderLeft, top + st::linkFont->ascent, cnt); p.drawText(_left + st::setHeaderLeft, top + st::linkFont->ascent, cnt);
} }
if (_localStorageHeight == 2) top += _localStorageClear.height() + st::setLittleSkip; if (_localStorageHeight == 2) top += _localStorageClear.height() + st::setLittleSkip;
@ -739,7 +739,7 @@ void SettingsInner::resizeEvent(QResizeEvent *e) {
// local storage // local storage
_localStorageClear.move(_left + st::setWidth - _localStorageClear.width(), top + st::setHeaderTop + st::setHeaderFont->ascent - st::linkFont->ascent); _localStorageClear.move(_left + st::setWidth - _localStorageClear.width(), top + st::setHeaderTop + st::setHeaderFont->ascent - st::linkFont->ascent);
top += st::setHeaderSkip; top += st::setHeaderSkip;
if ((Local::hasImages() || Local::hasStickers()) && Local::hasAudios()) { if ((Local::hasImages() || Local::hasStickers() || Local::hasWebFiles()) && Local::hasAudios()) {
_localStorageHeight = 2; _localStorageHeight = 2;
top += _localStorageClear.height() + st::setLittleSkip; top += _localStorageClear.height() + st::setLittleSkip;
} else { } else {

Просмотреть файл

@ -1260,7 +1260,7 @@ Window::TempDirState Window::localStorageState() {
if (_clearManager && _clearManager->hasTask(Local::ClearManagerStorage)) { if (_clearManager && _clearManager->hasTask(Local::ClearManagerStorage)) {
return TempDirRemoving; return TempDirRemoving;
} }
return (Local::hasImages() || Local::hasStickers() || Local::hasAudios()) ? TempDirExists : TempDirEmpty; return (Local::hasImages() || Local::hasStickers() || Local::hasWebFiles() || Local::hasAudios()) ? TempDirExists : TempDirEmpty;
} }
void Window::tempDirDelete(int task) { void Window::tempDirDelete(int task) {

Просмотреть файл

@ -34,8 +34,8 @@ IDI_ICON1 ICON "SourceFiles\\art\\icon256.ico"
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,9,15,5 FILEVERSION 0,9,15,6
PRODUCTVERSION 0,9,15,5 PRODUCTVERSION 0,9,15,6
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -51,10 +51,10 @@ BEGIN
BLOCK "040904b0" BLOCK "040904b0"
BEGIN BEGIN
VALUE "CompanyName", "Telegram Messenger LLP" VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileVersion", "0.9.15.5" VALUE "FileVersion", "0.9.15.6"
VALUE "LegalCopyright", "Copyright (C) 2013" VALUE "LegalCopyright", "Copyright (C) 2013"
VALUE "ProductName", "Telegram Desktop" VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "0.9.15.5" VALUE "ProductVersion", "0.9.15.6"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

Просмотреть файл

@ -3,4 +3,4 @@ AppVersionStrMajor 0.9
AppVersionStrSmall 0.9.15 AppVersionStrSmall 0.9.15
AppVersionStr 0.9.15 AppVersionStr 0.9.15
DevChannel 0 DevChannel 0
BetaVersion 9015005 BetaVersion 9015006