remembering last used inline bots, showing them in mentions dropdown, 9015006 beta

This commit is contained in:
John Preston 2016-01-01 22:48:32 +08:00
parent 85f46cef8c
commit cb2df51af6
15 changed files with 308 additions and 198 deletions

View file

@ -705,10 +705,11 @@ void Application::checkMapVersion() {
if (Local::oldMapVersion() < AppVersion) {
if (Local::oldMapVersion()) {
QString versionFeatures;
if (cDevVersion() && Local::oldMapVersion() < 9014) {
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));
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 = 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) {
// 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 {
versionFeatures = lang(lng_new_version_minor).trimmed();
}

View file

@ -23,7 +23,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
static const int32 AppVersion = 9015;
static const wchar_t *AppVersionStr = L"0.9.15";
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 *AppName = L"Telegram Desktop";
@ -87,6 +87,7 @@ enum {
AverageGifSize = 320 * 240,
WaitBeforeGifPause = 200, // wait 200ms for gif draw before pausing it
InlineBotRequestDelay = 400, // wait 400ms before context bot realtime request
RecentInlineBotsLimit = 10,
AVBlockSize = 4096, // 4Kb for ffmpeg blocksize

View file

@ -163,7 +163,7 @@ void DialogsInner::paintRegion(Painter &p, const QRegion &region, bool paintingO
PeerData *act = App::main()->activePeer();
MsgId actId = App::main()->activeMsgId();
for (; from < to; ++from) {
bool active = ((_filterResults[from]->history->peer == act) || (_filterResults[from]->history->peer->migrateTo() && _filterResults[from]->history->peer->migrateTo() == act)) && !actId;
bool active = ((_filterResults[from]->history->peer == act) || (_filterResults[from]->history->peer->migrateTo() && _filterResults[from]->history->peer->migrateTo() == act)) && !actId;
bool selected = (from == _filteredSel) || (_filterResults[from]->history->peer == _menuPeer);
_filterResults[from]->paint(p, w, active, selected, paintingOther);
p.translate(0, st::dlgHeight);
@ -872,7 +872,7 @@ void DialogsInner::onHashtagFilterUpdate(QStringRef newFilter) {
}
_hashtagFilter = newFilter.toString();
if (cRecentSearchHashtags().isEmpty() && cRecentWriteHashtags().isEmpty()) {
Local::readRecentHashtags();
Local::readRecentHashtagsAndBots();
}
const RecentHashtagPack &recent(cRecentSearchHashtags());
_hashtagResults.clear();
@ -1385,7 +1385,7 @@ void DialogsInner::loadPeerPhotos(int32 yFrom) {
if (from < _filterResults.size()) {
int32 to = (yTo / int32(st::dlgHeight)) + 1, w = width();
if (to > _filterResults.size()) to = _filterResults.size();
for (; from < to; ++from) {
_filterResults[from]->history->peer->photo->load();
}
@ -1434,7 +1434,7 @@ bool DialogsInner::choosePeer() {
}
}
cSetRecentSearchHashtags(recent);
Local::writeRecentHashtags();
Local::writeRecentHashtagsAndBots();
emit refreshHashtags();
selByMouse = true;
@ -1487,7 +1487,7 @@ void DialogsInner::saveRecentHashtags(const QString &text) {
}
}
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
Local::readRecentHashtags();
Local::readRecentHashtagsAndBots();
recent = cRecentSearchHashtags();
}
found = true;
@ -1495,7 +1495,7 @@ void DialogsInner::saveRecentHashtags(const QString &text) {
}
if (found) {
cSetRecentSearchHashtags(recent);
Local::writeRecentHashtags();
Local::writeRecentHashtagsAndBots();
}
}

View file

@ -3766,7 +3766,7 @@ void MentionsInner::paintEvent(QPaintEvent *e) {
if (selected) {
p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b);
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);
if (!_mrows->isEmpty()) {
@ -3902,6 +3902,10 @@ bool MentionsInner::select() {
return false;
}
void MentionsInner::setRecentInlineBotsInRows(int32 bots) {
_recentInlineBotsInRows = bots;
}
QString MentionsInner::getSelected() const {
int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size());
if (_sel >= 0 && _sel < maxSel) {
@ -3930,20 +3934,32 @@ void MentionsInner::mousePressEvent(QMouseEvent *e) {
_mouseSel = true;
onUpdateSelected(true);
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());
QString toRemove = _hrows->at(_sel);
RecentHashtagPack recent(cRecentWriteHashtags());
for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) {
if (i->first == toRemove) {
i = recent.erase(i);
} else {
++i;
bool removed = false;
if (_mrows->isEmpty()) {
QString toRemove = _hrows->at(_sel);
RecentHashtagPack &recent(cRefRecentWriteHashtags());
for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) {
if (i->first == toRemove) {
i = recent.erase(i);
removed = true;
} else {
++i;
}
}
} else {
UserData *toRemove = _mrows->at(_sel);
RecentInlineBots &recent(cRefRecentInlineBots());
int32 index = recent.indexOf(toRemove);
if (index >= 0) {
recent.remove(index);
removed = true;
}
}
cSetRecentWriteHashtags(recent);
Local::writeRecentHashtags();
if (removed) {
Local::writeRecentHashtagsAndBots();
}
_parent->updateFiltered();
_mouseSel = true;
@ -3980,8 +3996,8 @@ void MentionsInner::onUpdateSelected(bool force) {
if ((!force && !rect().contains(mouse)) || !_mouseSel) return;
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();
_overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= w - st::mentionHeight) : false;
if (sel < 0 || sel >= maxSel) {
sel = -1;
}
@ -4043,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();
_user = peer->asUser();
_channel = peer->asChannel();
start = start.toLower();
bool toDown = (_filter != start);
query = query.toLower();
bool toDown = (_filter != query);
if (toDown) {
_filter = start;
_filter = query;
}
_addInlineBots = start;
updateFiltered(toDown);
}
@ -4063,13 +4087,34 @@ bool MentionsDropdown::clearFilteredBotCommands() {
}
void MentionsDropdown::updateFiltered(bool toDown) {
int32 now = unixtime();
MentionRows rows;
int32 now = unixtime(), recentInlineBots = 0;
MentionRows mrows;
HashtagRows hrows;
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) {
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 (App::api()) App::api()->requestFullPeer(_chat);
} else if (!_chat->participants.isEmpty()) {
@ -4084,7 +4129,7 @@ void MentionsDropdown::updateFiltered(bool toDown) {
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;
rows.push_back(user);
mrows.push_back(user);
if (!ordered.isEmpty()) {
ordered.remove(App::onlineForSort(user, now), user);
}
@ -4092,7 +4137,7 @@ void MentionsDropdown::updateFiltered(bool toDown) {
if (!ordered.isEmpty()) {
for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
--i;
rows.push_back(i.value());
mrows.push_back(i.value());
}
}
} else if (_filter.at(0) == '@' && _channel && _channel->isMegagroup()) {
@ -4100,12 +4145,12 @@ void MentionsDropdown::updateFiltered(bool toDown) {
if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
if (App::api()) App::api()->requestLastParticipants(_channel);
} 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) {
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;
rows.push_back(user);
mrows.push_back(user);
}
}
} else if (_filter.at(0) == '#') {
@ -4184,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) {

View file

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

View file

@ -129,7 +129,7 @@ namespace Ui {
void showPeerHistoryAsync(const PeerId &peer, MsgId msgId) {
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));
}
}

View file

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

View file

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

View file

@ -5338,8 +5338,9 @@ void HistoryWidget::onCheckMentionDropdown() {
if (!_history || _a_show.animating()) return;
UserData *bot = _inlineBot;
QString start, inlineBotUsername(_inlineBotUsername);
_field.getMentionHashtagBotCommandStart(start, _inlineBot, _inlineBotUsername);
bool start = false;
QString inlineBotUsername(_inlineBotUsername);
QString query = _field.getMentionHashtagBotCommandPart(start, _inlineBot, _inlineBotUsername);
if (inlineBotUsername != _inlineBotUsername) {
if (_inlineBotResolveRequestId) {
Notify::inlineBotRequesting(false);
@ -5359,10 +5360,10 @@ void HistoryWidget::onCheckMentionDropdown() {
if (_inlineBot != bot) {
updateFieldPlaceholder();
}
if (_inlineBot->username == (cTestMode() ? qstr("contextbot") : qstr("gif")) && start.isEmpty()) {
if (_inlineBot->username == (cTestMode() ? qstr("contextbot") : qstr("gif")) && query.isEmpty()) {
_emojiPan.clearInlineBot();
} else {
_emojiPan.queryInlineBot(_inlineBot, start);
_emojiPan.queryInlineBot(_inlineBot, query);
}
if (!_attachMention.isHidden()) {
_attachMention.hideStart();
@ -5373,16 +5374,12 @@ void HistoryWidget::onCheckMentionDropdown() {
_field.finishPlaceholder();
}
_emojiPan.clearInlineBot();
if (!start.isEmpty()) {
if (start.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtags();
if (start.at(0) == '@' && _peer->isUser()) return;
if (start.at(0) == '/' && _peer->isUser() && !_peer->asUser()->botInfo) return;
_attachMention.showFiltered(_peer, start);
} else {
if (!_attachMention.isHidden()) {
_attachMention.hideStart();
}
if (!query.isEmpty()) {
if (query.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtagsAndBots();
if (query.at(0) == '@' && cRecentInlineBots().isEmpty()) Local::readRecentHashtagsAndBots();
if (query.at(0) == '/' && _peer->isUser() && !_peer->asUser()->botInfo) return;
}
_attachMention.showFiltered(_peer, query, start);
}
}
@ -6428,6 +6425,18 @@ void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) {
_saveDraftStart = getms();
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();
if (!_attachType.isHidden()) _attachType.hideStart();

View file

@ -538,22 +538,22 @@ namespace {
FileKey _dataNameKey = 0;
enum { // Local Storage Keys
lskUserMap = 0x00,
lskDraft = 0x01, // data: PeerId peer
lskDraftPosition = 0x02, // data: PeerId peer
lskImages = 0x03, // data: StorageKey location
lskLocations = 0x04, // no data
lskStickerImages = 0x05, // data: StorageKey location
lskAudios = 0x06, // data: StorageKey location
lskRecentStickersOld = 0x07, // no data
lskBackground = 0x08, // no data
lskUserSettings = 0x09, // no data
lskRecentHashtags = 0x0a, // no data
lskStickers = 0x0b, // no data
lskSavedPeers = 0x0c, // no data
lskReportSpamStatuses = 0x0d, // no data
lskSavedGifsOld = 0x0e,
lskSavedGifs = 0x0f,
lskUserMap = 0x00,
lskDraft = 0x01, // data: PeerId peer
lskDraftPosition = 0x02, // data: PeerId peer
lskImages = 0x03, // data: StorageKey location
lskLocations = 0x04, // no data
lskStickerImages = 0x05, // data: StorageKey location
lskAudios = 0x06, // data: StorageKey location
lskRecentStickersOld = 0x07, // no data
lskBackground = 0x08, // no data
lskUserSettings = 0x09, // no data
lskRecentHashtagsAndBots = 0x0a, // no data
lskStickers = 0x0b, // no data
lskSavedPeers = 0x0c, // no data
lskReportSpamStatuses = 0x0d, // no data
lskSavedGifsOld = 0x0e, // no data
lskSavedGifs = 0x0f, // no data
};
typedef QMap<PeerId, FileKey> DraftsMap;
@ -581,8 +581,8 @@ namespace {
bool _backgroundWasRead = false;
FileKey _userSettingsKey = 0;
FileKey _recentHashtagsKey = 0;
bool _recentHashtagsWereRead = false;
FileKey _recentHashtagsAndBotsKey = 0;
bool _recentHashtagsAndBotsWereRead = false;
FileKey _savedPeersKey = 0;
@ -726,18 +726,20 @@ namespace {
_fileLocationAliases.insert(MediaKey(kfirst, ksecond), MediaKey(vfirst, vsecond));
}
_storageWebFilesSize = 0;
_webFilesMap.clear();
if (!locations.stream.atEnd()) {
_storageWebFilesSize = 0;
_webFilesMap.clear();
quint32 webLocationsCount;
locations.stream >> webLocationsCount;
for (quint32 i = 0; i < webLocationsCount; ++i) {
QString url;
quint64 key;
qint32 size;
locations.stream >> url >> key >> size;
_webFilesMap.insert(url, FileDesc(key, size));
_storageWebFilesSize += size;
quint32 webLocationsCount;
locations.stream >> webLocationsCount;
for (quint32 i = 0; i < webLocationsCount; ++i) {
QString url;
quint64 key;
qint32 size;
locations.stream >> url >> key >> size;
_webFilesMap.insert(url, FileDesc(key, size));
_storageWebFilesSize += size;
}
}
}
}
@ -1667,7 +1669,7 @@ namespace {
qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0;
quint64 locationsKey = 0, reportSpamStatusesKey = 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()) {
quint32 keyType;
map.stream >> keyType;
@ -1744,8 +1746,8 @@ namespace {
case lskUserSettings: {
map.stream >> userSettingsKey;
} break;
case lskRecentHashtags: {
map.stream >> recentHashtagsKey;
case lskRecentHashtagsAndBots: {
map.stream >> recentHashtagsAndBotsKey;
} break;
case lskStickers: {
map.stream >> stickersKey;
@ -1788,7 +1790,7 @@ namespace {
_savedPeersKey = savedPeersKey;
_backgroundKey = backgroundKey;
_userSettingsKey = userSettingsKey;
_recentHashtagsKey = recentHashtagsKey;
_recentHashtagsAndBotsKey = recentHashtagsAndBotsKey;
_oldMapVersion = mapData.version;
if (_oldMapVersion < AppVersion) {
_mapChanged = true;
@ -1861,7 +1863,7 @@ namespace {
if (_savedPeersKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_backgroundKey) 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);
if (!_draftsMap.isEmpty()) {
mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size());
@ -1917,8 +1919,8 @@ namespace {
if (_userSettingsKey) {
mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey);
}
if (_recentHashtagsKey) {
mapData.stream << quint32(lskRecentHashtags) << quint64(_recentHashtagsKey);
if (_recentHashtagsAndBotsKey) {
mapData.stream << quint32(lskRecentHashtagsAndBots) << quint64(_recentHashtagsAndBotsKey);
}
map.writeEncrypted(mapData);
@ -2187,7 +2189,7 @@ namespace Local {
_storageWebFilesSize = 0;
_locationsKey = _reportSpamStatusesKey = 0;
_recentStickersKeyOld = _stickersKey = _savedGifsKey = 0;
_backgroundKey = _userSettingsKey = _recentHashtagsKey = _savedPeersKey = 0;
_backgroundKey = _userSettingsKey = _recentHashtagsAndBotsKey = _savedPeersKey = 0;
_oldMapVersion = _oldSettingsVersion = 0;
_mapChanged = true;
_writeMap(WriteMapNow);
@ -3258,89 +3260,6 @@ namespace Local {
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 result = sizeof(quint64) + sizeof(quint64) + _storageImageLocationSize();
if (peer->isUser()) {
@ -3370,7 +3289,7 @@ namespace Local {
return result;
}
void _writePeer(QDataStream &stream, PeerData *peer) {
void _writePeer(QDataStream &stream, PeerData *peer, int32 fileVersion = AppVersion) {
stream << quint64(peer->id) << quint64(peer->photoId);
_writeStorageImageLocation(stream, peer->photoLoc);
if (peer->isUser()) {
@ -3380,6 +3299,9 @@ namespace Local {
if (AppVersion >= 9012) {
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);
} else if (peer->isChat()) {
ChatData *chat = peer->asChat();
@ -3396,25 +3318,31 @@ namespace Local {
}
}
PeerData *_readPeer(FileReadDescriptor &from) {
PeerData *_readPeer(FileReadDescriptor &from, int32 fileVersion = 0) {
PeerData *result = 0;
quint64 peerId = 0, photoId = 0;
from.stream >> peerId >> photoId;
StorageImageLocation photoLoc(_readStorageImageLocation(from));
result = App::peerLoaded(peerId);
if (result && result->loaded) return result;
result = App::peer(peerId);
result->loaded = true;
if (result->isUser()) {
UserData *user = result->asUser();
QString first, last, phone, username;
QString first, last, phone, username, inlinePlaceholder;
quint64 access;
qint32 flags = 0, onlineTill, contact, botInfoVersion;
from.stream >> first >> last >> phone >> username >> access;
if (from.version >= 9012) {
from.stream >> flags;
}
if (from.version >= 9016 || fileVersion >= 9016) {
from.stream >> inlinePlaceholder;
}
from.stream >> onlineTill >> contact >> botInfoVersion;
bool showPhone = !isServiceUser(user->id) && (peerToUser(user->id) != MTP::authedId()) && (contact <= 0);
@ -3427,6 +3355,9 @@ namespace Local {
user->onlineTill = onlineTill;
user->contact = contact;
user->setBotInfoVersion(botInfoVersion);
if (!inlinePlaceholder.isEmpty() && user->botInfo) {
user->botInfo->inlinePlaceholder = inlinePlaceholder;
}
if (peerToUser(user->id) == MTP::authedId()) {
user->input = MTP_inputPeerSelf();
@ -3489,6 +3420,113 @@ namespace Local {
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() {
if (!_working()) return;
@ -3651,8 +3689,8 @@ namespace Local {
_stickersKey = 0;
_mapChanged = true;
}
if (_recentHashtagsKey) {
_recentHashtagsKey = 0;
if (_recentHashtagsAndBotsKey) {
_recentHashtagsAndBotsKey = 0;
_mapChanged = true;
}
if (_savedPeersKey) {

View file

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

View file

@ -1349,7 +1349,7 @@ void MainWidget::saveRecentHashtags(const QString &text) {
}
}
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
Local::readRecentHashtags();
Local::readRecentHashtagsAndBots();
recent = cRecentWriteHashtags();
}
found = true;
@ -1357,7 +1357,7 @@ void MainWidget::saveRecentHashtags(const QString &text) {
}
if (found) {
cSetRecentWriteHashtags(recent);
Local::writeRecentHashtags();
Local::writeRecentHashtagsAndBots();
}
}

View file

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

View file

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

View file

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