Suggest mentions not only by username, but by user names as well.

This commit is contained in:
John Preston 2016-05-08 19:11:47 +03:00
parent 3e5f51f45a
commit 90a3a80bf6
5 changed files with 124 additions and 50 deletions

View File

@ -73,12 +73,12 @@ void FieldAutocomplete::paintEvent(QPaintEvent *e) {
p.fillRect(rect(), st::white);
}
void FieldAutocomplete::showFiltered(PeerData *peer, QString query, bool start) {
// _inner->showFiltered(peer, query, start);
void FieldAutocomplete::showFiltered(PeerData *peer, QString query, bool addInlineBots) {
_chat = peer->asChat();
_user = peer->asUser();
_channel = peer->asChannel();
if (query.isEmpty()) {
_type = Type::Mentions;
rowsUpdated(internal::MentionRows(), internal::HashtagRows(), internal::BotCommandRows(), _srows, false);
return;
}
@ -86,11 +86,28 @@ void FieldAutocomplete::showFiltered(PeerData *peer, QString query, bool start)
_emoji = EmojiPtr();
query = query.toLower();
bool resetScroll = (_filter != query);
if (resetScroll) {
_filter = query;
auto type = Type::Stickers;
auto plainQuery = query.midRef(0);
switch (query.at(0).unicode()) {
case '@':
type = Type::Mentions;
plainQuery = query.midRef(1);
break;
case '#':
type = Type::Hashtags;
plainQuery = query.midRef(1);
break;
case '/':
type = Type::BotCommands;
plainQuery = query.midRef(1);
break;
}
_addInlineBots = start;
bool resetScroll = (_type != type || _filter != plainQuery);
if (resetScroll) {
_type = type;
_filter = plainQuery.toString();
}
_addInlineBots = addInlineBots;
updateFiltered(resetScroll);
}
@ -98,6 +115,7 @@ void FieldAutocomplete::showFiltered(PeerData *peer, QString query, bool start)
void FieldAutocomplete::showStickers(EmojiPtr emoji) {
bool resetScroll = (_emoji != emoji);
_emoji = emoji;
_type = Type::Stickers;
if (!emoji) {
rowsUpdated(_mrows, _hrows, _brows, StickerPack(), false);
return;
@ -158,23 +176,41 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
}
App::api()->requestStickerSets();
}
} else if (_filter.at(0) == '@') {
bool listAllSuggestions = (_filter.size() < 2);
} else if (_type == Type::Mentions) {
int maxListSize = _addInlineBots ? cRecentInlineBots().size() : 0;
if (_chat) {
mrows.reserve((_addInlineBots ? cRecentInlineBots().size() : 0) + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
maxListSize += (_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());
maxListSize += _channel->mgInfo->lastParticipants.size();
}
} else if (_addInlineBots) {
mrows.reserve(cRecentInlineBots().size());
}
if (maxListSize) {
mrows.reserve(maxListSize);
}
auto filterNotPassedByUsername = [this](UserData *user) -> bool {
if (user->username.startsWith(_filter, Qt::CaseInsensitive)) {
bool exactUsername = (user->username.size() == _filter.size());
return exactUsername;
}
return true;
};
auto filterNotPassedByName = [this](UserData *user) -> bool {
for_const (auto &namePart, user->names) {
if (namePart.startsWith(_filter, Qt::CaseInsensitive)) {
bool exactUsername = (user->username.compare(_filter, Qt::CaseInsensitive) == 0);
return exactUsername;
}
}
return true;
};
bool listAllSuggestions = _filter.isEmpty();
if (_addInlineBots) {
for (auto i = cRecentInlineBots().cbegin(), e = cRecentInlineBots().cend(); i != e; ++i) {
UserData *user = *i;
if (user->username.isEmpty()) continue;
if (!listAllSuggestions && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
for_const (auto user, cRecentInlineBots()) {
if (!listAllSuggestions && filterNotPassedByUsername(user)) continue;
mrows.push_back(user);
++recentInlineBots;
}
@ -187,16 +223,13 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
} else if (!_chat->participants.isEmpty()) {
for (auto i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
UserData *user = i.key();
if (!listAllSuggestions && user->username.isEmpty()) continue;
if (!listAllSuggestions && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
if (!listAllSuggestions && filterNotPassedByName(user)) continue;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
ordered.insertMulti(App::onlineForSort(user, now), user);
}
}
for (auto i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
UserData *user = *i;
if (!listAllSuggestions && user->username.isEmpty()) continue;
if (!listAllSuggestions && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
for_const (auto user, _chat->lastAuthors) {
if (!listAllSuggestions && filterNotPassedByName(user)) continue;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
mrows.push_back(user);
if (!ordered.isEmpty()) {
@ -215,24 +248,24 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
if (App::api()) App::api()->requestLastParticipants(_channel);
} else {
mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size());
for (auto i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.cend(); i != e; ++i) {
UserData *user = *i;
if (!listAllSuggestions && user->username.isEmpty()) continue;
if (!listAllSuggestions && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
for_const (auto user, _channel->mgInfo->lastParticipants) {
if (!listAllSuggestions && filterNotPassedByName(user)) continue;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
mrows.push_back(user);
}
}
}
} else if (_filter.at(0) == '#') {
} else if (_type == Type::Hashtags) {
bool listAllSuggestions = _filter.isEmpty();
auto &recent(cRecentWriteHashtags());
hrows.reserve(recent.size());
for (auto i = recent.cbegin(), e = recent.cend(); i != e; ++i) {
if (_filter.size() > 1 && (!i->first.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || i->first.size() + 1 == _filter.size())) continue;
if (!listAllSuggestions && (!i->first.startsWith(_filter, Qt::CaseInsensitive) || i->first.size() == _filter.size())) continue;
hrows.push_back(i->first);
}
} else if (_filter.at(0) == '/') {
bool hasUsername = _filter.indexOf('@') > 1;
} else if (_type == Type::BotCommands) {
bool listAllSuggestions = _filter.isEmpty();
bool hasUsername = _filter.indexOf('@') > 0;
QMap<UserData*, bool> bots;
int32 cnt = 0;
if (_chat) {
@ -277,9 +310,9 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
if (user->botInfo->commands.isEmpty()) continue;
bots.remove(user);
for (int32 j = 0, l = user->botInfo->commands.size(); j < l; ++j) {
if (_filter.size() > 1) {
if (!listAllSuggestions) {
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
if (!toFilter.startsWith(_filter.midRef(1), Qt::CaseInsensitive)/* || toFilter.size() + 1 == _filter.size()*/) continue;
if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) continue;
}
brows.push_back(qMakePair(user, &user->botInfo->commands.at(j)));
}
@ -289,9 +322,9 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
for (QMap<UserData*, bool>::const_iterator i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
UserData *user = i.key();
for (int32 j = 0, l = user->botInfo->commands.size(); j < l; ++j) {
if (_filter.size() > 1) {
if (!listAllSuggestions) {
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
if (!toFilter.startsWith(_filter.midRef(1), Qt::CaseInsensitive)/* || toFilter.size() + 1 == _filter.size()*/) continue;
if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) continue;
}
brows.push_back(qMakePair(user, &user->botInfo->commands.at(j)));
}
@ -552,9 +585,10 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
} else {
int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1;
int32 last = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
bool hasUsername = _parent->filter().indexOf('@') > 1;
int filterSize = qMax(_parent->filter().size() - 1, 0);
bool filterIsEmpty = (filterSize == 0);
auto filter = _parent->filter();
bool hasUsername = filter.indexOf('@') > 0;
int filterSize = filter.size();
bool filterIsEmpty = filter.isEmpty();
for (int32 i = from; i < to; ++i) {
if (i >= last) break;
@ -569,8 +603,8 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
p.setPen(st::black->p);
if (!_mrows->isEmpty()) {
UserData *user = _mrows->at(i);
QString first = filterIsEmpty ? QString() : ('@' + user->username.mid(0, filterSize));
QString second = (filterIsEmpty && !user->username.isEmpty()) ? ('@' + user->username) : user->username.mid(filterSize);
QString first = (!filterIsEmpty && user->username.startsWith(filter, Qt::CaseInsensitive)) ? ('@' + user->username.mid(0, filterSize)) : QString();
QString second = first.isEmpty() ? (user->username.isEmpty() ? QString() : ('@' + user->username)) : user->username.mid(filterSize);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second), unamewidth = firstwidth + secondwidth, namewidth = user->nameText.maxWidth();
if (mentionwidth < unamewidth + namewidth) {
namewidth = (mentionwidth * namewidth) / (namewidth + unamewidth);
@ -733,7 +767,7 @@ bool FieldAutocompleteInner::chooseSelected(FieldAutocomplete::ChooseMethod meth
UserData *user = _brows->at(_sel).first;
const BotCommand *command(_brows->at(_sel).second);
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (botStatus == 0 || botStatus == 2 || _parent->filter().indexOf('@') > 1) {
if (botStatus == 0 || botStatus == 2 || _parent->filter().indexOf('@') > 0) {
emit botCommandChosen('/' + command->command + '@' + user->username, method);
} else {
emit botCommandChosen('/' + command->command, method);

View File

@ -43,7 +43,7 @@ public:
void fastHide();
bool clearFilteredBotCommands();
void showFiltered(PeerData *peer, QString query, bool start);
void showFiltered(PeerData *peer, QString query, bool addInlineBots);
void showStickers(EmojiPtr emoji);
void setBoundings(QRect boundings);
@ -114,6 +114,13 @@ private:
UserData *_user;
ChannelData *_channel;
EmojiPtr _emoji;
enum class Type {
Mentions,
Hashtags,
BotCommands,
Stickers,
};
Type _type = Type::Mentions;
QString _filter;
QRect _boundings;
bool _addInlineBots;

View File

@ -3041,10 +3041,16 @@ void HistoryWidget::updateInlineBotQuery() {
return;
}
} else if (bot == LookingUpInlineBot) {
_inlineBot = LookingUpInlineBot;
return;
if (_inlineBot == LookingUpInlineBot) {
return;
}
bot = _inlineBot;
}
applyInlineBotQuery(bot, query);
}
void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) {
if (bot) {
if (_inlineBot != bot) {
_inlineBot = bot;
@ -5651,12 +5657,32 @@ void HistoryWidget::inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result)
_inlineBotResolveRequestId = 0;
// Notify::inlineBotRequesting(false);
_inlineBotUsername = QString();
UserData *resolvedBot = nullptr;
if (result.type() == mtpc_contacts_resolvedPeer) {
const auto &d(result.c_contacts_resolvedPeer());
App::feedUsers(d.vusers);
if (resolvedBot = App::feedUsers(d.vusers)) {
if (!resolvedBot->botInfo || resolvedBot->botInfo->inlinePlaceholder.isEmpty()) {
resolvedBot = nullptr;
}
}
App::feedChats(d.vchats);
}
updateInlineBotQuery();
UserData *bot = nullptr;
QString inlineBotUsername;
QString query = _field.getInlineBotQuery(&bot, &inlineBotUsername);
if (inlineBotUsername == _inlineBotUsername) {
if (bot == LookingUpInlineBot) {
bot = resolvedBot;
}
} else {
bot = nullptr;
}
if (bot) {
applyInlineBotQuery(bot, query);
} else {
clearInlineBot();
}
}
bool HistoryWidget::inlineBotResolveFail(QString name, const RPCError &error) {
@ -6097,7 +6123,8 @@ void HistoryWidget::onCheckFieldAutocomplete() {
if (!_history || _a_show.animating()) return;
bool start = false;
QString query = _inlineBot ? QString() : _field.getMentionHashtagBotCommandPart(start);
bool isInlineBot = _inlineBot && (_inlineBot != LookingUpInlineBot);
QString query = isInlineBot ? QString() : _field.getMentionHashtagBotCommandPart(start);
if (!query.isEmpty()) {
if (query.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtagsAndBots();
if (query.at(0) == '@' && cRecentInlineBots().isEmpty()) Local::readRecentHashtagsAndBots();

View File

@ -548,7 +548,6 @@ public:
void destroyData();
void updateFieldPlaceholder();
void updateInlineBotQuery();
void updateStickersByEmoji();
void uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, const QString &source = QString(), bool withText = false);
@ -820,6 +819,12 @@ private:
void clearInlineBot();
void inlineBotChanged();
// Look in the _field for the inline bot and query string.
void updateInlineBotQuery();
// Request to show results in the emoji panel.
void applyInlineBotQuery(UserData *bot, const QString &query);
MsgId _replyToId = 0;
Text _replyToName;
int _replyToNameVersion = 0;

View File

@ -465,12 +465,13 @@ void FlatTextarea::insertTag(const QString &text, QString tagId) {
(i < 2 || !(fragmentText.at(i - 2).isLetterOrNumber() || fragmentText.at(i - 2) == '_'))) {
cursor.setPosition(fragmentPosition + i - 1);
int till = fragmentPosition + i;
for (; (till < fragmentEnd) && (till - fragmentPosition - i + 1 < text.size()); ++till) {
if (fragmentText.at(till - fragmentPosition).toLower() != text.at(till - fragmentPosition - i + 1).toLower()) {
for (; (till < fragmentEnd); ++till) {
auto ch = fragmentText.at(till - fragmentPosition);
if (!ch.isLetterOrNumber() && ch != '_') {
break;
}
}
if (till - fragmentPosition - i + 1 == text.size() && till < fragmentEnd && fragmentText.at(till - fragmentPosition) == ' ') {
if (till < fragmentEnd && fragmentText.at(till - fragmentPosition) == ' ') {
++till;
}
cursor.setPosition(till, QTextCursor::KeepAnchor);