diff --git a/src/Cache_p.h b/src/Cache_p.h
index 51dc4cbc..1e388e77 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -405,6 +405,50 @@ private:
event);
}
+ template<typename T>
+ std::optional<mtx::events::StateEvent<T>> getStateEvent(lmdb::txn txn,
+ const std::string &room_id,
+ std::string_view state_key = "")
+ {
+ constexpr auto type = mtx::events::state_content_to_type<T>;
+ static_assert(type != mtx::events::EventType::Unsupported,
+ "Not a supported type in state events.");
+
+ if (room_id.empty())
+ return std::nullopt;
+
+ std::string_view value;
+ if (state_key.empty()) {
+ auto db = getStatesDb(txn, room_id);
+ if (!db.get(txn, to_string(type), value)) {
+ return std::nullopt;
+ }
+ } else {
+ auto db = getStatesKeyDb(txn, room_id);
+ std::string d = json::object({{"key", state_key}}).dump();
+ std::string_view data = d;
+
+ auto cursor = lmdb::cursor::open(txn, db);
+ if (!cursor.get(state_key, data, MDB_GET_BOTH))
+ return std::nullopt;
+
+ try {
+ auto eventsDb = getEventsDb(txn, room_id);
+ if (!eventsDb.get(
+ txn, json::parse(data)["id"].get<std::string>(), value))
+ return std::nullopt;
+ } catch (std::exception &e) {
+ return std::nullopt;
+ }
+ }
+
+ try {
+ return json::parse(value).get<mtx::events::StateEvent<T>>();
+ } catch (std::exception &e) {
+ return std::nullopt;
+ }
+ }
+
void saveInvites(lmdb::txn &txn,
const std::map<std::string, mtx::responses::InvitedRoom> &rooms);
diff --git a/src/CompletionProxyModel.cpp b/src/CompletionProxyModel.cpp
index 2341c292..412708a2 100644
--- a/src/CompletionProxyModel.cpp
+++ b/src/CompletionProxyModel.cpp
@@ -12,16 +12,18 @@
CompletionProxyModel::CompletionProxyModel(QAbstractItemModel *model,
int max_mistakes,
+ size_t max_completions,
QObject *parent)
: QAbstractProxyModel(parent)
, maxMistakes_(max_mistakes)
+ , max_completions_(max_completions)
{
setSourceModel(model);
QRegularExpression splitPoints("\\s+|-");
// insert all the full texts
for (int i = 0; i < sourceModel()->rowCount(); i++) {
- if (i < 7)
+ if (static_cast<size_t>(i) < max_completions_)
mapping.push_back(i);
auto string1 = sourceModel()
@@ -82,14 +84,9 @@ CompletionProxyModel::invalidate()
{
auto key = searchString_.toUcs4();
beginResetModel();
- mapping = trie_.search(key, 7, maxMistakes_);
+ if (!key.empty()) // return default model data, if no search string
+ mapping = trie_.search(key, max_completions_, maxMistakes_);
endResetModel();
-
- std::string temp;
- for (auto v : mapping) {
- temp += std::to_string(v) + ", ";
- }
- nhlog::ui()->debug("mapping: {}", temp);
}
QHash<int, QByteArray>
@@ -101,12 +98,22 @@ CompletionProxyModel::roleNames() const
int
CompletionProxyModel::rowCount(const QModelIndex &) const
{
- return (int)mapping.size();
+ if (searchString_.isEmpty())
+ return std::min(static_cast<int>(std::min<size_t>(max_completions_,
+ std::numeric_limits<int>::max())),
+ sourceModel()->rowCount());
+ else
+ return (int)mapping.size();
}
QModelIndex
CompletionProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
+ // return default model data, if no search string
+ if (searchString_.isEmpty()) {
+ return index(sourceIndex.row(), 0);
+ }
+
for (int i = 0; i < (int)mapping.size(); i++) {
if (mapping[i] == sourceIndex.row()) {
return index(i, 0);
@@ -119,6 +126,12 @@ QModelIndex
CompletionProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
auto row = proxyIndex.row();
+
+ // return default model data, if no search string
+ if (searchString_.isEmpty()) {
+ return index(row, 0);
+ }
+
if (row < 0 || row >= (int)mapping.size())
return QModelIndex();
diff --git a/src/CompletionProxyModel.h b/src/CompletionProxyModel.h
index f19e4d93..d85d9343 100644
--- a/src/CompletionProxyModel.h
+++ b/src/CompletionProxyModel.h
@@ -153,8 +153,9 @@ class CompletionProxyModel : public QAbstractProxyModel
QString searchString READ searchString WRITE setSearchString NOTIFY newSearchString)
public:
CompletionProxyModel(QAbstractItemModel *model,
- int max_mistakes = 2,
- QObject *parent = nullptr);
+ int max_mistakes = 2,
+ size_t max_completions = 7,
+ QObject *parent = nullptr);
void invalidate();
@@ -184,4 +185,5 @@ private:
trie<uint, int> trie_;
std::vector<int> mapping;
int maxMistakes_;
+ size_t max_completions_;
};
diff --git a/src/emoji/EmojiModel.cpp b/src/emoji/EmojiModel.cpp
index 70b85934..66e7aeda 100644
--- a/src/emoji/EmojiModel.cpp
+++ b/src/emoji/EmojiModel.cpp
@@ -11,6 +11,20 @@
using namespace emoji;
+int
+EmojiModel::categoryToIndex(int category)
+{
+ auto dist = std::distance(Provider::emoji.begin(),
+ std::lower_bound(Provider::emoji.begin(),
+ Provider::emoji.end(),
+ static_cast<Emoji::Category>(category),
+ [](const struct Emoji &e, Emoji::Category c) {
+ return e.category < c;
+ }));
+
+ return static_cast<int>(dist);
+}
+
QHash<int, QByteArray>
EmojiModel::roleNames() const
{
@@ -60,59 +74,3 @@ EmojiModel::data(const QModelIndex &index, int role) const
return {};
}
-
-EmojiProxyModel::EmojiProxyModel(QObject *parent)
- : QSortFilterProxyModel(parent)
-{}
-
-EmojiProxyModel::~EmojiProxyModel() {}
-
-Emoji::Category
-EmojiProxyModel::category() const
-{
- return category_;
-}
-
-void
-EmojiProxyModel::setCategory(Emoji::Category cat)
-{
- if (category_ == cat) {
- return;
- }
-
- category_ = cat;
- emit categoryChanged();
-
- invalidateFilter();
-}
-
-QString
-EmojiProxyModel::filter() const
-{
- return filterRegExp().pattern();
-}
-
-void
-EmojiProxyModel::setFilter(const QString &filter)
-{
- if (filterRegExp().pattern() == filter) {
- return;
- }
-
- setFilterWildcard(filter);
- emit filterChanged();
-}
-
-bool
-EmojiProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
-{
- const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
- const Emoji emoji = index.data(static_cast<int>(EmojiModel::Roles::Emoji)).value<Emoji>();
-
- // TODO: Add favorites / recently used
- if (category_ != Emoji::Category::Search) {
- return emoji.category == category_;
- }
-
- return filterRegExp().isEmpty() ? true : filterRegExp().indexIn(emoji.shortName) != -1;
-}
diff --git a/src/emoji/EmojiModel.h b/src/emoji/EmojiModel.h
index 1a8bf029..679563f1 100644
--- a/src/emoji/EmojiModel.h
+++ b/src/emoji/EmojiModel.h
@@ -30,38 +30,10 @@ public:
using QAbstractListModel::QAbstractListModel;
+ Q_INVOKABLE int categoryToIndex(int category);
+
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
};
-
-class EmojiProxyModel : public QSortFilterProxyModel
-{
- Q_OBJECT
-
- Q_PROPERTY(
- emoji::Emoji::Category category READ category WRITE setCategory NOTIFY categoryChanged)
- Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged)
-
-public:
- explicit EmojiProxyModel(QObject *parent = nullptr);
- ~EmojiProxyModel() override;
-
- Emoji::Category category() const;
- void setCategory(Emoji::Category cat);
-
- QString filter() const;
- void setFilter(const QString &filter);
-
-signals:
- void categoryChanged();
- void filterChanged();
-
-protected:
- bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
-
-private:
- Emoji::Category category_ = Emoji::Category::Search;
- emoji::Provider emoji_provider_;
-};
}
diff --git a/src/emoji/EmojiSearchModel.h b/src/emoji/EmojiSearchModel.h
deleted file mode 100644
index 64af83dd..00000000
--- a/src/emoji/EmojiSearchModel.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include "EmojiModel.h"
-
-#include <CompletionModelRoles.h>
-#include <QDebug>
-#include <QEvent>
-#include <QSortFilterProxyModel>
-
-namespace emoji {
-
-// Map emoji data to searchable data
-class EmojiSearchModel : public QSortFilterProxyModel
-{
-public:
- EmojiSearchModel(QObject *parent = nullptr)
- : QSortFilterProxyModel(parent)
- {
- setSourceModel(new EmojiModel(this));
- }
- QVariant data(const QModelIndex &index, int role = Qt::UserRole + 1) const override
- {
- switch (role) {
- case Qt::DisplayRole: {
- auto emoji = QSortFilterProxyModel::data(index, role).toString();
- return emoji + " :" +
- toShortcode(data(index, EmojiModel::ShortName).toString()) + ":";
- }
- case CompletionModel::CompletionRole:
- return QSortFilterProxyModel::data(index, EmojiModel::Unicode);
- case CompletionModel::SearchRole: {
- return toShortcode(
- QSortFilterProxyModel::data(index, EmojiModel::ShortName).toString());
- }
- default:
- return QSortFilterProxyModel::data(index, role);
- }
- }
-
-private:
- QString toShortcode(QString shortname) const
- {
- return shortname.replace(" ", "-").replace(":", "-").replace("--", "-").toLower();
- }
-};
-}
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index f15b0b14..e9986c7e 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -172,9 +172,6 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
qRegisterMetaType<std::vector<DeviceInfo>>();
qmlRegisterType<emoji::EmojiModel>("im.nheko.EmojiModel", 1, 0, "EmojiModel");
- qmlRegisterType<emoji::EmojiProxyModel>("im.nheko.EmojiModel", 1, 0, "EmojiProxyModel");
- qmlRegisterUncreatableType<QAbstractItemModel>(
- "im.nheko.EmojiModel", 1, 0, "QAbstractItemModel", "Used by proxy models");
qmlRegisterUncreatableType<emoji::Emoji>(
"im.nheko.EmojiModel", 1, 0, "Emoji", "Used by emoji models");
qmlRegisterUncreatableMetaObject(emoji::staticMetaObject,
@@ -595,6 +592,11 @@ TimelineViewManager::completerFor(QString completerName, QString roomId)
auto proxy = new CompletionProxyModel(emojiModel);
emojiModel->setParent(proxy);
return proxy;
+ } else if (completerName == "allemoji") {
+ auto emojiModel = new emoji::EmojiModel();
+ auto proxy = new CompletionProxyModel(emojiModel, 1, static_cast<size_t>(-1) / 4);
+ emojiModel->setParent(proxy);
+ return proxy;
} else if (completerName == "room") {
auto roomModel = new RoomsModel(false);
auto proxy = new CompletionProxyModel(roomModel, 4);
|