diff --git a/CHANGELOG.md b/CHANGELOG.md index fc5ca5edc..b0a352055 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ - Minor: Don't show update button for nightly builds on macOS and Linux, this was already the case for Windows (#2163, #2164) - Minor: Tab and split titles now use display/localized channel names (#2189) - Minor: Add a setting to limit the amount of historical messages loaded from the Recent Messages API (#2250, #2252) +- Minor: Made username autocompletion truecase (#1199, #1883) - Bugfix: Fix crash occurring when pressing Escape in the Color Picker Dialog (#1843) - Bugfix: Fix bug where the "check user follow state" event could trigger a network request requesting the user to follow or unfollow a user. By itself its quite harmless as it just repeats to Twitch the same follow state we had, so no follows should have been lost by this but it meant there was a rogue network request that was fired that could cause a crash (#1906) - Bugfix: /usercard command will now respect the "Automatically close user popup" setting (#1918) diff --git a/CMakeLists.txt b/CMakeLists.txt index 29094f702..de20932b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,9 @@ if (BUILD_TESTS) target_link_libraries(chatterino-test PajladaSerialize) + set_property(TARGET chatterino-test PROPERTY CXX_STANDARD 17) + set_property(TARGET chatterino-test PROPERTY CXX_STANDARD_REQUIRED ON) + gtest_discover_tests(chatterino-test) else() message(FATAL_ERROR "This cmake file is only intended for tests right now. Use qmake to build chatterino2") diff --git a/src/common/ChannelChatters.cpp b/src/common/ChannelChatters.cpp index 4728aef94..3657aeeec 100644 --- a/src/common/ChannelChatters.cpp +++ b/src/common/ChannelChatters.cpp @@ -67,7 +67,7 @@ void ChannelChatters::addPartedUser(const QString &user) void ChannelChatters::setChatters(UsernameSet &&set) { - *this->chatters_.access() = set; + this->chatters_.access()->merge(std::move(set)); } const QColor ChannelChatters::getUserColor(const QString &user) diff --git a/src/common/UsernameSet.cpp b/src/common/UsernameSet.cpp index 958a6fc3a..1c0474e93 100644 --- a/src/common/UsernameSet.cpp +++ b/src/common/UsernameSet.cpp @@ -4,6 +4,30 @@ namespace chatterino { +namespace { + + std::pair findOrErase( + std::set &set, const QString &value) + { + if (!value.isLower()) + { + auto iter = set.find(value); + if (iter != set.end()) + { + if (QString::compare(*iter, value, Qt::CaseSensitive) != 0) + { + set.erase(iter); + } + else + { + return {iter, false}; + } + } + } + return {set.end(), true}; + } +} // namespace + // // UsernameSet // @@ -43,15 +67,25 @@ std::set::size_type UsernameSet::size() const std::pair UsernameSet::insert(const QString &value) { - this->insertPrefix(value); + auto pair = findOrErase(this->items, value); + if (!pair.second) + { + return pair; + } + this->insertPrefix(value); return this->items.insert(value); } std::pair UsernameSet::insert(QString &&value) { - this->insertPrefix(value); + auto pair = findOrErase(this->items, value); + if (!pair.second) + { + return pair; + } + this->insertPrefix(value); return this->items.insert(std::move(value)); } @@ -68,6 +102,24 @@ bool UsernameSet::contains(const QString &value) const return this->items.count(value) == 1; } +void UsernameSet::merge(UsernameSet &&set) +{ + for (auto it = this->items.begin(); it != this->items.end();) + { + auto iter = set.items.find(*it); + if (iter == set.items.end()) + { + it = this->items.erase(it); + } + else + { + ++it; + } + } + this->items.merge(set.items); + this->firstKeyForPrefix = set.firstKeyForPrefix; +} + // // Range // diff --git a/src/common/UsernameSet.hpp b/src/common/UsernameSet.hpp index f43916cc4..1268695ee 100644 --- a/src/common/UsernameSet.hpp +++ b/src/common/UsernameSet.hpp @@ -77,6 +77,7 @@ public: std::pair insert(QString &&value); bool contains(const QString &value) const; + void merge(UsernameSet &&set); private: void insertPrefix(const QString &string); diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 5b3029690..ce75e02fa 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -804,6 +804,7 @@ void TwitchChannel::fetchDisplayName() .arg(channel->getName()) .arg(user.displayName)); } + channel->addRecentChatter(channel->getDisplayName()); channel->displayNameChanged.invoke(); }, [] {}); diff --git a/tests/src/UsernameSet.cpp b/tests/src/UsernameSet.cpp index 7098941bf..fad210520 100644 --- a/tests/src/UsernameSet.cpp +++ b/tests/src/UsernameSet.cpp @@ -44,17 +44,40 @@ TEST(UsernameSet, insert) EXPECT_EQ(set.size(), 1); + // Non-lowercase variant should override full lowercase variant + p = set.insert("PAJLADA"); + EXPECT_TRUE(p.second); + + EXPECT_EQ(set.size(), 1); + + // Lowercase variant should not override non-lowercase variant + p = set.insert("pajlada"); + EXPECT_FALSE(p.second); + + EXPECT_EQ(set.size(), 1); + p = set.insert("pajbot"); EXPECT_TRUE(p.second); EXPECT_EQ(set.size(), 2); - p = set.insert("pajlada"); + p = set.insert("pajbot"); EXPECT_FALSE(p.second); EXPECT_EQ(set.size(), 2); - p = set.insert("PAJLADA"); + p = set.insert("Pajbot"); + EXPECT_TRUE(p.second); + + EXPECT_EQ(set.size(), 2); + + p = set.insert("PAJBOT"); + EXPECT_TRUE(p.second); + + EXPECT_EQ(set.size(), 2); + + // Same uppercase should not result in a change + p = set.insert("PAJBOT"); EXPECT_FALSE(p.second); EXPECT_EQ(set.size(), 2);