diff --git a/src/channel.cpp b/src/channel.cpp index 3e214bb34..f72a0a01d 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -22,11 +22,19 @@ Channel::Channel(const QString &_name) : name(_name) , completionModel(this->name) { + this->clearCompletionModelTimer = new QTimer; + QObject::connect(this->clearCompletionModelTimer, &QTimer::timeout, [this]() { + this->completionModel.ClearExpiredStrings(); // + }); + this->clearCompletionModelTimer->start(60 * 1000); } Channel::~Channel() { this->destroyed.invoke(); + + this->clearCompletionModelTimer->stop(); + this->clearCompletionModelTimer->deleteLater(); } bool Channel::isEmpty() const diff --git a/src/channel.hpp b/src/channel.hpp index f7993c7f4..ca51c7c27 100644 --- a/src/channel.hpp +++ b/src/channel.hpp @@ -7,6 +7,7 @@ #include "util/concurrentmap.hpp" #include +#include #include #include @@ -18,6 +19,8 @@ struct Message; class Channel : public std::enable_shared_from_this { + QTimer *clearCompletionModelTimer; + public: explicit Channel(const QString &_name); virtual ~Channel(); diff --git a/src/util/completionmodel.cpp b/src/util/completionmodel.cpp index a13b917f1..0bef743b1 100644 --- a/src/util/completionmodel.cpp +++ b/src/util/completionmodel.cpp @@ -81,12 +81,13 @@ void CompletionModel::refresh() void CompletionModel::addString(const std::string &str, TaggedString::Type type) { - // Always add a space at the end of completions - this->emotes.insert({qS(str + " "), type}); + this->addString(qS(str), type); } void CompletionModel::addString(const QString &str, TaggedString::Type type) { + std::lock_guard lock(this->emotesMutex); + // Always add a space at the end of completions this->emotes.insert({str + " ", type}); } @@ -108,4 +109,22 @@ void CompletionModel::addUser(const QString &str) } } +void CompletionModel::ClearExpiredStrings() +{ + std::lock_guard lock(this->emotesMutex); + + auto now = std::chrono::steady_clock::now(); + + for (auto it = this->emotes.begin(); it != this->emotes.end();) { + const auto &taggedString = *it; + + if (taggedString.HasExpired(now)) { + // debug::Log("String {} expired", taggedString.str); + it = this->emotes.erase(it); + } else { + ++it; + } + } +} + } // namespace chatterino diff --git a/src/util/completionmodel.hpp b/src/util/completionmodel.hpp index b804b5246..629f5dd15 100644 --- a/src/util/completionmodel.hpp +++ b/src/util/completionmodel.hpp @@ -4,6 +4,8 @@ #include +#include +#include #include #include @@ -12,9 +14,6 @@ namespace chatterino { class CompletionModel : public QAbstractListModel { struct TaggedString { - QString str; - - // Type will help decide the lifetime of the tagged strings enum Type { Username, @@ -26,7 +25,38 @@ class CompletionModel : public QAbstractListModel TwitchGlobalEmote, TwitchSubscriberEmote, Emoji, - } type; + }; + + TaggedString(const QString &_str, Type _type) + : str(_str) + , type(_type) + , timeAdded(std::chrono::steady_clock::now()) + { + } + + QString str; + + // Type will help decide the lifetime of the tagged strings + Type type; + + std::chrono::steady_clock::time_point timeAdded; + + bool HasExpired(const std::chrono::steady_clock::time_point &now) const + { + switch (this->type) { + case Type::Username: { + static std::chrono::minutes expirationTimer(10); + + return (this->timeAdded + expirationTimer < now); + } break; + + default: { + return false; + } break; + } + + return false; + } bool IsEmote() const { @@ -40,45 +70,49 @@ class CompletionModel : public QAbstractListModel int k = QString::compare(this->str, that.str, Qt::CaseInsensitive); if (k == 0) { return this->str > that.str; - } else { - return k < 0; - } - } else { - return true; - } - } else { - if (that.IsEmote()) { - return false; - } else { - int k = QString::compare(this->str, that.str, Qt::CaseInsensitive); - if (k == 0) { - return false; - } else { - return k < 0; } + + return k < 0; } + + return true; } + + if (that.IsEmote()) { + return false; + } + + int k = QString::compare(this->str, that.str, Qt::CaseInsensitive); + if (k == 0) { + return false; + } + + return k < 0; } }; public: CompletionModel(const QString &_channelName); - virtual int columnCount(const QModelIndex &) const override + int columnCount(const QModelIndex &) const override { return 1; } - virtual QVariant data(const QModelIndex &index, int) const override + QVariant data(const QModelIndex &index, int) const override { + std::lock_guard lock(this->emotesMutex); + // TODO: Implement more safely auto it = this->emotes.begin(); std::advance(it, index.row()); return QVariant(it->str); } - virtual int rowCount(const QModelIndex &) const override + int rowCount(const QModelIndex &) const override { + std::lock_guard lock(this->emotesMutex); + return this->emotes.size(); } @@ -88,12 +122,15 @@ public: void addUser(const QString &str); + void ClearExpiredStrings(); + private: TaggedString createUser(const QString &str) { return TaggedString{str, TaggedString::Type::Username}; } + mutable std::mutex emotesMutex; std::set emotes; QString channelName;