From 12ce7686ce8a7cae411c280d30a12934b8707550 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Wed, 16 Jun 2021 00:09:45 +0200 Subject: Show some spaces in the community sidebar --- src/Cache.cpp | 321 ++++++++++++++++++++++++++++---------- src/Cache.h | 6 - src/CacheStructs.h | 2 + src/Cache_p.h | 72 +++++++-- src/timeline/CommunitiesModel.cpp | 42 +++-- src/timeline/CommunitiesModel.h | 4 + 6 files changed, 341 insertions(+), 106 deletions(-) (limited to 'src') diff --git a/src/Cache.cpp b/src/Cache.cpp index 2178bbfb..0bd6fe0d 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -55,6 +55,10 @@ constexpr auto BATCH_SIZE = 100; //! Format: room_id -> RoomInfo constexpr auto ROOMS_DB("rooms"); constexpr auto INVITES_DB("invites"); +//! maps each room to its parent space (id->id) +constexpr auto SPACES_PARENTS_DB("space_parents"); +//! maps each space to its current children (id->id) +constexpr auto SPACES_CHILDREN_DB("space_children"); //! Information that must be kept between sync requests. constexpr auto SYNC_STATE_DB("sync_state"); //! Read receipts per room/event. @@ -237,12 +241,14 @@ Cache::setup() env_.open(cacheDirectory_.toStdString().c_str()); } - auto txn = lmdb::txn::begin(env_); - syncStateDb_ = lmdb::dbi::open(txn, SYNC_STATE_DB, MDB_CREATE); - roomsDb_ = lmdb::dbi::open(txn, ROOMS_DB, MDB_CREATE); - invitesDb_ = lmdb::dbi::open(txn, INVITES_DB, MDB_CREATE); - readReceiptsDb_ = lmdb::dbi::open(txn, READ_RECEIPTS_DB, MDB_CREATE); - notificationsDb_ = lmdb::dbi::open(txn, NOTIFICATIONS_DB, MDB_CREATE); + auto txn = lmdb::txn::begin(env_); + syncStateDb_ = lmdb::dbi::open(txn, SYNC_STATE_DB, MDB_CREATE); + roomsDb_ = lmdb::dbi::open(txn, ROOMS_DB, MDB_CREATE); + spacesChildrenDb_ = lmdb::dbi::open(txn, SPACES_CHILDREN_DB, MDB_CREATE | MDB_DUPSORT); + spacesParentsDb_ = lmdb::dbi::open(txn, SPACES_PARENTS_DB, MDB_CREATE | MDB_DUPSORT); + invitesDb_ = lmdb::dbi::open(txn, INVITES_DB, MDB_CREATE); + readReceiptsDb_ = lmdb::dbi::open(txn, READ_RECEIPTS_DB, MDB_CREATE); + notificationsDb_ = lmdb::dbi::open(txn, NOTIFICATIONS_DB, MDB_CREATE); // Device management devicesDb_ = lmdb::dbi::open(txn, DEVICES_DB, MDB_CREATE); @@ -1194,6 +1200,9 @@ Cache::saveState(const mtx::responses::Sync &res) auto userKeyCacheDb = getUserKeysDb(txn); + std::set spaces_with_updates; + std::set rooms_with_space_updates; + // Save joined rooms for (const auto &room : res.rooms.join) { auto statesdb = getStatesDb(txn, room.first); @@ -1212,6 +1221,41 @@ Cache::saveState(const mtx::responses::Sync &res) updatedInfo.topic = getRoomTopic(txn, statesdb).toStdString(); updatedInfo.avatar_url = getRoomAvatarUrl(txn, statesdb, membersdb).toStdString(); updatedInfo.version = getRoomVersion(txn, statesdb).toStdString(); + updatedInfo.is_space = getRoomIsSpace(txn, statesdb); + + if (updatedInfo.is_space) { + bool space_updates = false; + for (const auto &e : room.second.state.events) + if (std::holds_alternative>(e) || + std::holds_alternative>(e)) + space_updates = true; + for (const auto &e : room.second.timeline.events) + if (std::holds_alternative>(e) || + std::holds_alternative>(e)) + space_updates = true; + + if (space_updates) + spaces_with_updates.insert(room.first); + } + + { + bool room_has_space_update = false; + for (const auto &e : room.second.state.events) { + if (auto se = std::get_if>(&e)) { + spaces_with_updates.insert(se->state_key); + room_has_space_update = true; + } + } + for (const auto &e : room.second.timeline.events) { + if (auto se = std::get_if>(&e)) { + spaces_with_updates.insert(se->state_key); + room_has_space_update = true; + } + } + + if (room_has_space_update) + rooms_with_space_updates.insert(room.first); + } bool has_new_tags = false; // Process the account_data associated with this room @@ -1291,6 +1335,8 @@ Cache::saveState(const mtx::responses::Sync &res) removeLeftRooms(txn, res.rooms.leave); + updateSpaces(txn, spaces_with_updates, std::move(rooms_with_space_updates)); + txn.commit(); std::map readStatus; @@ -1339,6 +1385,7 @@ Cache::saveInvites(lmdb::txn &txn, const std::map -Cache::roomsWithTagUpdates(const mtx::responses::Sync &res) -{ - using namespace mtx::events; - - std::vector rooms; - for (const auto &room : res.rooms.join) { - bool hasUpdates = false; - for (const auto &evt : room.second.account_data.events) { - if (std::holds_alternative>(evt)) { - hasUpdates = true; - } - } - - if (hasUpdates) - rooms.emplace_back(room.first); - } - - return rooms; -} - RoomInfo Cache::singleRoomInfo(const std::string &room_id) { @@ -2337,6 +2363,29 @@ Cache::getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb) return QString("1"); } +bool +Cache::getRoomIsSpace(lmdb::txn &txn, lmdb::dbi &statesdb) +{ + using namespace mtx::events; + using namespace mtx::events::state; + + std::string_view event; + bool res = statesdb.get(txn, to_string(mtx::events::EventType::RoomCreate), event); + + if (res) { + try { + StateEvent msg = json::parse(event); + + return msg.content.type == mtx::events::state::room_type::space; + } catch (const json::exception &e) { + nhlog::db()->warn("failed to parse m.room.create event: {}", e.what()); + } + } + + nhlog::db()->warn("m.room.create event is missing room version, assuming version \"1\""); + return false; +} + std::optional Cache::getRoomAliases(const std::string &roomid) { @@ -2464,6 +2513,27 @@ Cache::getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &db) return QString(); } +bool +Cache::getInviteRoomIsSpace(lmdb::txn &txn, lmdb::dbi &db) +{ + using namespace mtx::events; + using namespace mtx::events::state; + + std::string_view event; + bool res = db.get(txn, to_string(mtx::events::EventType::RoomCreate), event); + + if (res) { + try { + StrippedEvent msg = json::parse(event); + return msg.content.type == mtx::events::state::room_type::space; + } catch (const json::exception &e) { + nhlog::db()->warn("failed to parse m.room.topic event: {}", e.what()); + } + } + + return false; +} + std::vector Cache::joinedRooms() { @@ -2506,42 +2576,6 @@ Cache::getMember(const std::string &room_id, const std::string &user_id) return std::nullopt; } -std::vector -Cache::searchRooms(const std::string &query, std::uint8_t max_items) -{ - std::multimap> items; - - auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - auto cursor = lmdb::cursor::open(txn, roomsDb_); - - std::string_view room_id, room_data; - while (cursor.get(room_id, room_data, MDB_NEXT)) { - RoomInfo tmp = json::parse(room_data); - - const int score = utils::levenshtein_distance( - query, QString::fromStdString(tmp.name).toLower().toStdString()); - items.emplace(score, std::make_pair(room_id, tmp)); - } - - cursor.close(); - - auto end = items.begin(); - - if (items.size() >= max_items) - std::advance(end, max_items); - else if (items.size() > 0) - std::advance(end, items.size()); - - std::vector results; - for (auto it = items.begin(); it != end; it++) { - results.push_back(RoomSearchResult{it->second.first, it->second.second}); - } - - txn.commit(); - - return results; -} - std::vector Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_t len) { @@ -3203,6 +3237,147 @@ Cache::deleteOldData() noexcept } } +void +Cache::updateSpaces(lmdb::txn &txn, + const std::set &spaces_with_updates, + std::set rooms_with_updates) +{ + if (spaces_with_updates.empty() && rooms_with_updates.empty()) + return; + + for (const auto &space : spaces_with_updates) { + // delete old entries + { + auto cursor = lmdb::cursor::open(txn, spacesChildrenDb_); + bool first = true; + std::string_view sp = space, space_child = ""; + + if (cursor.get(sp, space_child, MDB_SET)) { + while (cursor.get( + sp, space_child, first ? MDB_FIRST_DUP : MDB_NEXT_DUP)) { + first = false; + spacesParentsDb_.del(txn, space_child, space); + } + } + cursor.close(); + spacesChildrenDb_.del(txn, space); + } + + for (const auto &event : + getStateEventsWithType(txn, space)) { + if (event.content.via.has_value() && event.state_key.size() > 3 && + event.state_key.at(0) == '!') { + spacesChildrenDb_.put(txn, space, event.state_key); + spacesParentsDb_.put(txn, event.state_key, space); + } + } + } + + const auto space_event_type = to_string(mtx::events::EventType::RoomPowerLevels); + + for (const auto &room : rooms_with_updates) { + for (const auto &event : + getStateEventsWithType(txn, room)) { + if (event.content.via.has_value() && event.state_key.size() > 3 && + event.state_key.at(0) == '!') { + const std::string &space = event.state_key; + + auto pls = + getStateEvent(txn, space); + + if (!pls) + continue; + + if (pls->content.user_level(event.sender) >= + pls->content.state_level(space_event_type)) { + spacesChildrenDb_.put(txn, space, room); + spacesParentsDb_.put(txn, room, space); + } + } + } + } +} + +QMap> +Cache::spaces() +{ + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + + QMap> ret; + { + auto cursor = lmdb::cursor::open(txn, spacesChildrenDb_); + bool first = true; + std::string_view space_id, space_child; + while (cursor.get(space_id, space_child, first ? MDB_FIRST : MDB_NEXT)) { + first = false; + + if (!space_child.empty()) { + std::string_view room_data; + if (roomsDb_.get(txn, space_id, room_data)) { + RoomInfo tmp = json::parse(std::move(room_data)); + ret.insert( + QString::fromUtf8(space_id.data(), space_id.size()), tmp); + } else { + ret.insert( + QString::fromUtf8(space_id.data(), space_id.size()), + std::nullopt); + } + } + } + cursor.close(); + } + + return ret; +} + +std::vector +Cache::getParentRoomIds(const std::string &room_id) +{ + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + + std::vector roomids; + { + auto cursor = lmdb::cursor::open(txn, spacesParentsDb_); + bool first = true; + std::string_view sp = room_id, space_parent; + if (cursor.get(sp, space_parent, MDB_SET)) { + while (cursor.get(sp, space_parent, first ? MDB_FIRST_DUP : MDB_NEXT_DUP)) { + first = false; + + if (!space_parent.empty()) + roomids.emplace_back(space_parent); + } + } + cursor.close(); + } + + return roomids; +} + +std::vector +Cache::getChildRoomIds(const std::string &room_id) +{ + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + + std::vector roomids; + { + auto cursor = lmdb::cursor::open(txn, spacesChildrenDb_); + bool first = true; + std::string_view sp = room_id, space_child; + if (cursor.get(sp, space_child, MDB_SET)) { + while (cursor.get(sp, space_child, first ? MDB_FIRST_DUP : MDB_NEXT_DUP)) { + first = false; + + if (!space_child.empty()) + roomids.emplace_back(space_child); + } + } + cursor.close(); + } + + return roomids; +} + std::optional Cache::getAccountData(lmdb::txn &txn, mtx::events::EventType type, const std::string &room_id) { @@ -3884,6 +4059,7 @@ to_json(json &j, const RoomInfo &info) j["avatar_url"] = info.avatar_url; j["version"] = info.version; j["is_invite"] = info.is_invite; + j["is_space"] = info.is_space; j["join_rule"] = info.join_rule; j["guest_access"] = info.guest_access; @@ -3903,6 +4079,7 @@ from_json(const json &j, RoomInfo &info) info.version = j.value( "version", QCoreApplication::translate("RoomInfo", "no version stored").toStdString()); info.is_invite = j.at("is_invite"); + info.is_space = j.value("is_space", false); info.join_rule = j.at("join_rule"); info.guest_access = j.at("guest_access"); @@ -4158,12 +4335,6 @@ getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) return instance_->getRoomAvatarUrl(txn, statesdb, membersdb); } -QString -getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb) -{ - return instance_->getRoomVersion(txn, statesdb); -} - std::vector getMembers(const std::string &room_id, std::size_t startIndex, std::size_t len) { @@ -4305,11 +4476,7 @@ roomsWithStateUpdates(const mtx::responses::Sync &res) { return instance_->roomsWithStateUpdates(res); } -std::vector -roomsWithTagUpdates(const mtx::responses::Sync &res) -{ - return instance_->roomsWithTagUpdates(res); -} + std::map getRoomInfo(const std::vector &rooms) { @@ -4329,12 +4496,6 @@ calculateRoomReadStatus() instance_->calculateRoomReadStatus(); } -std::vector -searchRooms(const std::string &query, std::uint8_t max_items) -{ - return instance_->searchRooms(query, max_items); -} - void markSentNotification(const std::string &event_id) { diff --git a/src/Cache.h b/src/Cache.h index 74ec9695..b0520f6b 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -79,9 +79,6 @@ getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb); //! Retrieve the room avatar's url if any. QString getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); -//! Retrieve the version of the room if any. -QString -getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb); //! Retrieve member info from a room. std::vector @@ -166,9 +163,6 @@ calculateRoomReadStatus(const std::string &room_id); void calculateRoomReadStatus(); -std::vector -searchRooms(const std::string &query, std::uint8_t max_items = 5); - void markSentNotification(const std::string &event_id); //! Removes an event from the sent notifications. diff --git a/src/CacheStructs.h b/src/CacheStructs.h index f7d6f0e2..1d0f0d70 100644 --- a/src/CacheStructs.h +++ b/src/CacheStructs.h @@ -76,6 +76,8 @@ struct RoomInfo std::string version; //! Whether or not the room is an invite. bool is_invite = false; + //! Wheter or not the room is a space + bool is_space = false; //! Total number of members in the room. size_t member_count = 0; //! Who can access to the room. diff --git a/src/Cache_p.h b/src/Cache_p.h index 669f1895..064f4882 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -72,6 +72,7 @@ public: std::optional getRoomAliases(const std::string &roomid); QHash invites(); std::optional invite(std::string_view roomid); + QMap> spaces(); //! Calculate & return the name of the room. QString getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); @@ -84,6 +85,8 @@ public: QString getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); //! Retrieve the version of the room if any. QString getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb); + //! Retrieve if the room is a space + bool getRoomIsSpace(lmdb::txn &txn, lmdb::dbi &statesdb); //! Get a specific state event template @@ -146,7 +149,6 @@ public: RoomInfo singleRoomInfo(const std::string &room_id); std::vector roomsWithStateUpdates(const mtx::responses::Sync &res); - std::vector roomsWithTagUpdates(const mtx::responses::Sync &res); std::map getRoomInfo(const std::vector &rooms); //! Calculates which the read status of a room. @@ -154,9 +156,6 @@ public: bool calculateRoomReadStatus(const std::string &room_id); void calculateRoomReadStatus(); - std::vector searchRooms(const std::string &query, - std::uint8_t max_items = 5); - void markSentNotification(const std::string &event_id); //! Removes an event from the sent notifications. void removeReadNotification(const std::string &event_id); @@ -222,6 +221,8 @@ public: void deleteOldData() noexcept; //! Retrieve all saved room ids. std::vector getRoomIds(lmdb::txn &txn); + std::vector getParentRoomIds(const std::string &room_id); + std::vector getChildRoomIds(const std::string &room_id); //! Mark a room that uses e2e encryption. void setEncryptedRoom(lmdb::txn &txn, const std::string &room_id); @@ -327,6 +328,7 @@ private: QString getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); QString getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb); QString getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); + bool getInviteRoomIsSpace(lmdb::txn &txn, lmdb::dbi &db); std::optional getMember(const std::string &room_id, const std::string &user_id); @@ -430,20 +432,22 @@ private: if (room_id.empty()) return std::nullopt; + const auto typeStr = to_string(type); std::string_view value; if (state_key.empty()) { auto db = getStatesDb(txn, room_id); - if (!db.get(txn, to_string(type), value)) { + if (!db.get(txn, typeStr, 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 db = getStatesKeyDb(txn, room_id); + std::string d = json::object({{"key", state_key}}).dump(); + std::string_view data = d; + std::string_view typeStrV = typeStr; auto cursor = lmdb::cursor::open(txn, db); - if (!cursor.get(state_key, data, MDB_GET_BOTH)) + if (!cursor.get(typeStrV, data, MDB_GET_BOTH)) return std::nullopt; try { @@ -463,6 +467,47 @@ private: } } + template + std::vector> getStateEventsWithType(lmdb::txn &txn, + const std::string &room_id) + + { + constexpr auto type = mtx::events::state_content_to_type; + static_assert(type != mtx::events::EventType::Unsupported, + "Not a supported type in state events."); + + if (room_id.empty()) + return {}; + + std::vector> events; + + { + auto db = getStatesKeyDb(txn, room_id); + auto eventsDb = getEventsDb(txn, room_id); + const auto typeStr = to_string(type); + std::string_view typeStrV = typeStr; + std::string_view data; + std::string_view value; + + auto cursor = lmdb::cursor::open(txn, db); + bool first = true; + if (cursor.get(typeStrV, data, MDB_SET)) { + while (cursor.get( + typeStrV, data, first ? MDB_FIRST_DUP : MDB_NEXT_DUP)) { + first = false; + + if (eventsDb.get(txn, + json::parse(data)["id"].get(), + value)) + events.push_back( + json::parse(value) + .get>()); + } + } + } + + return events; + } void saveInvites(lmdb::txn &txn, const std::map &rooms); @@ -482,6 +527,10 @@ private: } } + void updateSpaces(lmdb::txn &txn, + const std::set &spaces_with_updates, + std::set rooms_with_updates); + lmdb::dbi getPendingReceiptsDb(lmdb::txn &txn) { return lmdb::dbi::open(txn, "pending_receipts", MDB_CREATE); @@ -548,8 +597,8 @@ private: lmdb::dbi getStatesKeyDb(lmdb::txn &txn, const std::string &room_id) { - auto db = - lmdb::dbi::open(txn, std::string(room_id + "/state_by_key").c_str(), MDB_CREATE); + auto db = lmdb::dbi::open( + txn, std::string(room_id + "/state_by_key").c_str(), MDB_CREATE | MDB_DUPSORT); lmdb::dbi_set_dupsort(txn, db, compare_state_key); return db; } @@ -611,6 +660,7 @@ private: lmdb::env env_; lmdb::dbi syncStateDb_; lmdb::dbi roomsDb_; + lmdb::dbi spacesChildrenDb_, spacesParentsDb_; lmdb::dbi invitesDb_; lmdb::dbi readReceiptsDb_; lmdb::dbi notificationsDb_; diff --git a/src/timeline/CommunitiesModel.cpp b/src/timeline/CommunitiesModel.cpp index 6c236784..88464bf9 100644 --- a/src/timeline/CommunitiesModel.cpp +++ b/src/timeline/CommunitiesModel.cpp @@ -44,8 +44,23 @@ CommunitiesModel::data(const QModelIndex &index, int role) const case CommunitiesModel::Roles::Id: return ""; } - } else if (index.row() - 1 < tags_.size()) { - auto tag = tags_.at(index.row() - 1); + } else if (index.row() - 1 < spaceOrder_.size()) { + auto id = spaceOrder_.at(index.row() - 1); + switch (role) { + case CommunitiesModel::Roles::AvatarUrl: + return QString::fromStdString(spaces_.at(id).avatar_url); + case CommunitiesModel::Roles::DisplayName: + case CommunitiesModel::Roles::Tooltip: + return QString::fromStdString(spaces_.at(id).name); + case CommunitiesModel::Roles::ChildrenHidden: + return true; + case CommunitiesModel::Roles::Hidden: + return hiddentTagIds_.contains("space:" + id); + case CommunitiesModel::Roles::Id: + return "space:" + id; + } + } else if (index.row() - 1 < tags_.size() + spaceOrder_.size()) { + auto tag = tags_.at(index.row() - 1 - spaceOrder_.size()); if (tag == "m.favourite") { switch (role) { case CommunitiesModel::Roles::AvatarUrl: @@ -78,7 +93,6 @@ CommunitiesModel::data(const QModelIndex &index, int role) const case CommunitiesModel::Roles::AvatarUrl: return QString(":/icons/icons/ui/tag.png"); case CommunitiesModel::Roles::DisplayName: - return tag.mid(2); case CommunitiesModel::Roles::Tooltip: return tag.mid(2); } @@ -99,17 +113,27 @@ CommunitiesModel::data(const QModelIndex &index, int role) const void CommunitiesModel::initializeSidebar() { + beginResetModel(); + tags_.clear(); + spaceOrder_.clear(); + spaces_.clear(); + std::set ts; - for (const auto &e : cache::roomInfo()) { - for (const auto &t : e.tags) { - if (t.find("u.") == 0 || t.find("m." == 0)) { - ts.insert(t); + std::vector tempSpaces; + auto infos = cache::roomInfo(); + for (auto it = infos.begin(); it != infos.end(); it++) { + if (it.value().is_space) { + spaceOrder_.push_back(it.key()); + spaces_[it.key()] = it.value(); + } else { + for (const auto &t : it.value().tags) { + if (t.find("u.") == 0 || t.find("m." == 0)) { + ts.insert(t); + } } } } - beginResetModel(); - tags_.clear(); for (const auto &t : ts) tags_.push_back(QString::fromStdString(t)); diff --git a/src/timeline/CommunitiesModel.h b/src/timeline/CommunitiesModel.h index 66d6b21b..8c40ec5b 100644 --- a/src/timeline/CommunitiesModel.h +++ b/src/timeline/CommunitiesModel.h @@ -11,6 +11,8 @@ #include +#include "CacheStructs.h" + class CommunitiesModel : public QAbstractListModel { Q_OBJECT @@ -71,4 +73,6 @@ private: QStringList tags_; QString currentTagId_; QStringList hiddentTagIds_; + QStringList spaceOrder_; + std::map spaces_; }; -- cgit 1.5.1 From 6bfa6c4c793f1cd74df64d593ff2060ff94f176f Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Wed, 16 Jun 2021 22:59:41 +0200 Subject: Allow filtering by space --- src/Cache.cpp | 36 +++++++++++++++++++++++++----------- src/Cache_p.h | 12 +++++++++--- src/timeline/CommunitiesModel.cpp | 9 +++++++++ src/timeline/RoomlistModel.cpp | 24 ++++++++++++++++++++++++ src/timeline/RoomlistModel.h | 3 +++ 5 files changed, 70 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/Cache.cpp b/src/Cache.cpp index 0bd6fe0d..8b1798d6 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -905,7 +905,9 @@ Cache::runMigrations() std::reverse(oldMessages.events.begin(), oldMessages.events.end()); // save messages using the new method - saveTimelineMessages(txn, room_id, oldMessages); + auto eventsDb = getEventsDb(txn, room_id); + saveTimelineMessages( + txn, eventsDb, room_id, oldMessages); } // delete old messages db @@ -1208,13 +1210,24 @@ Cache::saveState(const mtx::responses::Sync &res) auto statesdb = getStatesDb(txn, room.first); auto stateskeydb = getStatesKeyDb(txn, room.first); auto membersdb = getMembersDb(txn, room.first); - - saveStateEvents( - txn, statesdb, stateskeydb, membersdb, room.first, room.second.state.events); - saveStateEvents( - txn, statesdb, stateskeydb, membersdb, room.first, room.second.timeline.events); - - saveTimelineMessages(txn, room.first, room.second.timeline); + auto eventsDb = getEventsDb(txn, room.first); + + saveStateEvents(txn, + statesdb, + stateskeydb, + membersdb, + eventsDb, + room.first, + room.second.state.events); + saveStateEvents(txn, + statesdb, + stateskeydb, + membersdb, + eventsDb, + room.first, + room.second.timeline.events); + + saveTimelineMessages(txn, eventsDb, room.first, room.second.timeline); RoomInfo updatedInfo; updatedInfo.name = getRoomName(txn, statesdb, membersdb).toStdString(); @@ -2634,11 +2647,12 @@ void Cache::savePendingMessage(const std::string &room_id, const mtx::events::collections::TimelineEvent &message) { - auto txn = lmdb::txn::begin(env_); + auto txn = lmdb::txn::begin(env_); + auto eventsDb = getEventsDb(txn, room_id); mtx::responses::Timeline timeline; timeline.events.push_back(message.data); - saveTimelineMessages(txn, room_id, timeline); + saveTimelineMessages(txn, eventsDb, room_id, timeline); auto pending = getPendingMessagesDb(txn, room_id); @@ -2706,13 +2720,13 @@ Cache::removePendingStatus(const std::string &room_id, const std::string &txn_id void Cache::saveTimelineMessages(lmdb::txn &txn, + lmdb::dbi &eventsDb, const std::string &room_id, const mtx::responses::Timeline &res) { if (res.events.empty()) return; - auto eventsDb = getEventsDb(txn, room_id); auto relationsDb = getRelationsDb(txn, room_id); auto orderDb = getEventOrderDb(txn, room_id); diff --git a/src/Cache_p.h b/src/Cache_p.h index 064f4882..e35e78ec 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -335,6 +335,7 @@ private: std::string getLastEventId(lmdb::txn &txn, const std::string &room_id); DescInfo getLastMessageInfo(lmdb::txn &txn, const std::string &room_id); void saveTimelineMessages(lmdb::txn &txn, + lmdb::dbi &eventsDb, const std::string &room_id, const mtx::responses::Timeline &res); @@ -353,11 +354,12 @@ private: lmdb::dbi &statesdb, lmdb::dbi &stateskeydb, lmdb::dbi &membersdb, + lmdb::dbi &eventsDb, const std::string &room_id, const std::vector &events) { for (const auto &e : events) - saveStateEvent(txn, statesdb, stateskeydb, membersdb, room_id, e); + saveStateEvent(txn, statesdb, stateskeydb, membersdb, eventsDb, room_id, e); } template @@ -365,6 +367,7 @@ private: lmdb::dbi &statesdb, lmdb::dbi &stateskeydb, lmdb::dbi &membersdb, + lmdb::dbi &eventsDb, const std::string &room_id, const T &event) { @@ -401,8 +404,10 @@ private: } std::visit( - [&txn, &statesdb, &stateskeydb](auto e) { - if constexpr (isStateEvent(e)) + [&txn, &statesdb, &stateskeydb, &eventsDb](auto e) { + if constexpr (isStateEvent(e)) { + eventsDb.put(txn, e.event_id, json(e).dump()); + if (e.type != EventType::Unsupported) { if (e.state_key.empty()) statesdb.put( @@ -417,6 +422,7 @@ private: }) .dump()); } + } }, event); } diff --git a/src/timeline/CommunitiesModel.cpp b/src/timeline/CommunitiesModel.cpp index 88464bf9..6ff953d2 100644 --- a/src/timeline/CommunitiesModel.cpp +++ b/src/timeline/CommunitiesModel.cpp @@ -185,6 +185,15 @@ CommunitiesModel::setCurrentTagId(QString tagId) return; } } + } else if (tagId.startsWith("space:")) { + auto tag = tagId.mid(6); + for (const auto &t : spaceOrder_) { + if (t == tag) { + this->currentTagId_ = tagId; + emit currentTagIdChanged(currentTagId_); + return; + } + } } this->currentTagId_ = ""; diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 0f980c6c..3b6ad54a 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -530,6 +530,30 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons return false; } return true; + } else if (filterType == FilterBy::Space) { + auto roomid = sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::RoomId) + .toString(); + auto tags = sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::Tags) + .toStringList(); + + auto contains = [](const std::vector &v, const std::string &str) { + for (const auto &e : v) + if (e == str) + return true; + return false; + }; + auto parents = cache::client()->getParentRoomIds(roomid.toStdString()); + + if (!contains(parents, filterStr.toStdString())) + return false; + else if (!hiddenTags.empty()) { + for (const auto &t : tags) + if (hiddenTags.contains(t)) + return false; + } + return true; } else { return true; } diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h index b0244886..5f8b8bd8 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h @@ -134,6 +134,9 @@ public slots: if (tagId.startsWith("tag:")) { filterType = FilterBy::Tag; filterStr = tagId.mid(4); + } else if (tagId.startsWith("space:")) { + filterType = FilterBy::Space; + filterStr = tagId.mid(6); } else { filterType = FilterBy::Nothing; filterStr.clear(); -- cgit 1.5.1 From 0ec7be3090814c7a61273cfe88e5dff9fbaccd91 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 18 Jun 2021 12:10:13 +0200 Subject: Fix hiding rooms from a space --- src/timeline/CommunitiesModel.cpp | 8 +++++- src/timeline/RoomlistModel.cpp | 54 ++++++++++++++++++++++++++++++--------- src/timeline/RoomlistModel.h | 1 + 3 files changed, 50 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/timeline/CommunitiesModel.cpp b/src/timeline/CommunitiesModel.cpp index 6ff953d2..c66d5949 100644 --- a/src/timeline/CommunitiesModel.cpp +++ b/src/timeline/CommunitiesModel.cpp @@ -214,7 +214,13 @@ CommunitiesModel::toggleTagId(QString tagId) if (tagId.startsWith("tag:")) { auto idx = tags_.indexOf(tagId.mid(4)); if (idx != -1) - emit dataChanged(index(idx), index(idx), {Hidden}); + emit dataChanged(index(idx + 1 + spaceOrder_.size()), + index(idx + 1 + spaceOrder_.size()), + {Hidden}); + } else if (tagId.startsWith("space:")) { + auto idx = spaceOrder_.indexOf(tagId.mid(6)); + if (idx != -1) + emit dataChanged(index(idx + 1), index(idx + 1), {Hidden}); } emit hiddenTagsChanged(); diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 3b6ad54a..0d9ec66b 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -51,6 +51,7 @@ RoomlistModel::roleNames() const {IsInvite, "isInvite"}, {IsSpace, "isSpace"}, {Tags, "tags"}, + {ParentSpaces, "parentSpaces"}, }; } @@ -93,6 +94,14 @@ RoomlistModel::data(const QModelIndex &index, int role) const list.push_back(QString::fromStdString(t)); return list; } + case Roles::ParentSpaces: { + auto parents = + cache::client()->getParentRoomIds(roomid.toStdString()); + QStringList list; + for (const auto &t : parents) + list.push_back(QString::fromStdString(t)); + return list; + } default: return {}; } @@ -122,6 +131,14 @@ RoomlistModel::data(const QModelIndex &index, int role) const return false; case Roles::Tags: return QStringList(); + case Roles::ParentSpaces: { + auto parents = + cache::client()->getParentRoomIds(roomid.toStdString()); + QStringList list; + for (const auto &t : parents) + list.push_back(QString::fromStdString(t)); + return list; + } default: return {}; } @@ -514,6 +531,14 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons for (const auto &t : tags) if (hiddenTags.contains(t)) return false; + } else if (!hiddenSpaces.empty()) { + auto parents = + sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::ParentSpaces) + .toStringList(); + for (const auto &t : parents) + if (hiddenSpaces.contains(t)) + return false; } return true; @@ -528,30 +553,35 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons for (const auto &t : tags) if (t != filterStr && hiddenTags.contains(t)) return false; + } else if (!hiddenSpaces.empty()) { + auto parents = + sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::ParentSpaces) + .toStringList(); + for (const auto &t : parents) + if (hiddenSpaces.contains(t)) + return false; } return true; } else if (filterType == FilterBy::Space) { - auto roomid = sourceModel() - ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::RoomId) - .toString(); + auto parents = + sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::ParentSpaces) + .toStringList(); auto tags = sourceModel() ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::Tags) .toStringList(); - auto contains = [](const std::vector &v, const std::string &str) { - for (const auto &e : v) - if (e == str) - return true; - return false; - }; - auto parents = cache::client()->getParentRoomIds(roomid.toStdString()); - - if (!contains(parents, filterStr.toStdString())) + if (!parents.contains(filterStr)) return false; else if (!hiddenTags.empty()) { for (const auto &t : tags) if (hiddenTags.contains(t)) return false; + } else if (!hiddenSpaces.empty()) { + for (const auto &t : parents) + if (hiddenSpaces.contains(t)) + return false; } return true; } else { diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h index 5f8b8bd8..d6cbb462 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h @@ -38,6 +38,7 @@ public: IsInvite, IsSpace, Tags, + ParentSpaces, }; RoomlistModel(TimelineViewManager *parent = nullptr); -- cgit 1.5.1 From f349b0cce0335c2c9f6aaabfad9315e80bc72677 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 18 Jun 2021 14:05:52 +0200 Subject: Hide spaces by default, unless they are in the current space filter --- src/timeline/CommunitiesModel.cpp | 19 +++++++++++++++++++ src/timeline/RoomlistModel.cpp | 34 ++++++++++++++++++++++++++++++---- src/timeline/TimelineModel.cpp | 4 ++++ src/timeline/TimelineModel.h | 9 ++++++--- 4 files changed, 59 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/timeline/CommunitiesModel.cpp b/src/timeline/CommunitiesModel.cpp index c66d5949..97bfa76d 100644 --- a/src/timeline/CommunitiesModel.cpp +++ b/src/timeline/CommunitiesModel.cpp @@ -167,6 +167,25 @@ CommunitiesModel::sync(const mtx::responses::Rooms &rooms) mtx::events::AccountDataEvent>(e)) { tagsUpdated = true; } + for (const auto &e : room.state.events) + if (std::holds_alternative< + mtx::events::StateEvent>(e) || + std::holds_alternative< + mtx::events::StateEvent>(e)) { + tagsUpdated = true; + } + for (const auto &e : room.timeline.events) + if (std::holds_alternative< + mtx::events::StateEvent>(e) || + std::holds_alternative< + mtx::events::StateEvent>(e)) { + tagsUpdated = true; + } + } + for (const auto &[roomid, room] : rooms.leave) { + (void)room; + if (spaceOrder_.contains(QString::fromStdString(roomid))) + tagsUpdated = true; } if (tagsUpdated) diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 0d9ec66b..e4014333 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -85,8 +85,9 @@ RoomlistModel::data(const QModelIndex &index, int role) const case Roles::NotificationCount: return room->notificationCount(); case Roles::IsInvite: - case Roles::IsSpace: return false; + case Roles::IsSpace: + return room->isSpace(); case Roles::Tags: { auto info = cache::singleRoomInfo(roomid.toStdString()); QStringList list; @@ -429,7 +430,9 @@ enum NotificationImportance : short AllEventsRead = 0, NewMessage = 1, NewMentions = 2, - Invite = 3 + Invite = 3, + SubSpace = 4, + CurrentSpace = 5, }; } @@ -439,7 +442,13 @@ FilteredRoomlistModel::calculateImportance(const QModelIndex &idx) const // Returns the degree of importance of the unread messages in the room. // If sorting by importance is disabled in settings, this only ever // returns ImportanceDisabled or Invite - if (sourceModel()->data(idx, RoomlistModel::IsInvite).toBool()) { + if (sourceModel()->data(idx, RoomlistModel::IsSpace).toBool()) { + if (filterType == FilterBy::Space && + filterStr == sourceModel()->data(idx, RoomlistModel::RoomId).toString()) + return CurrentSpace; + else + return SubSpace; + } else if (sourceModel()->data(idx, RoomlistModel::IsInvite).toBool()) { return Invite; } else if (!this->sortByImportance) { return ImportanceDisabled; @@ -539,6 +548,10 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons for (const auto &t : parents) if (hiddenSpaces.contains(t)) return false; + } else if (sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) + .toBool()) { + return false; } return true; @@ -561,6 +574,10 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons for (const auto &t : parents) if (hiddenSpaces.contains(t)) return false; + } else if (sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) + .toBool()) { + return false; } return true; } else if (filterType == FilterBy::Space) { @@ -572,7 +589,11 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::Tags) .toStringList(); - if (!parents.contains(filterStr)) + if (filterStr == sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::RoomId) + .toString()) + return true; + else if (!parents.contains(filterStr)) return false; else if (!hiddenTags.empty()) { for (const auto &t : tags) @@ -582,6 +603,11 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons for (const auto &t : parents) if (hiddenSpaces.contains(t)) return false; + } else if (sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) + .toBool() && + !parents.contains(filterStr)) { + return false; } return true; } else { diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 99547b15..1ecb6cdf 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -320,6 +320,10 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj { lastMessage_.timestamp = 0; + if (auto create = + cache::client()->getStateEvent(room_id.toStdString())) + this->isSpace_ = create->content.type == mtx::events::state::room_type::space; + connect( this, &TimelineModel::redactionFailed, diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 3ebbe120..42aa136f 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -161,6 +161,7 @@ class TimelineModel : public QAbstractListModel Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged) Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY roomAvatarUrlChanged) Q_PROPERTY(QString roomTopic READ roomTopic NOTIFY roomTopicChanged) + Q_PROPERTY(bool isSpace READ isSpace CONSTANT) Q_PROPERTY(InputBar *input READ input CONSTANT) Q_PROPERTY(Permissions *permissions READ permissions NOTIFY permissionsChanged) @@ -262,6 +263,7 @@ public: RelatedInfo relatedInfo(QString id); DescInfo lastMessage() const { return lastMessage_; } + bool isSpace() const { return isSpace_; } public slots: void setCurrentIndex(int index); @@ -366,9 +368,6 @@ private: QString room_id_; - bool decryptDescription = true; - bool m_paginationInProgress = false; - QString currentId, currentReadId; QString reply_, edit_; QString textBeforeEdit, replyBeforeEdit; @@ -388,6 +387,10 @@ private: friend struct SendMessageVisitor; int notification_count = 0, highlight_count = 0; + + bool decryptDescription = true; + bool m_paginationInProgress = false; + bool isSpace_ = false; }; template -- cgit 1.5.1 From c69d2ef64886ee4946abf4b959d53b884014ce7a Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 18 Jun 2021 14:13:01 +0200 Subject: Fix off by 1 in previousRoom condition --- src/timeline/RoomlistModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index e4014333..a44959fc 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -662,7 +662,7 @@ FilteredRoomlistModel::previousRoom() if (r) { int idx = roomidToIndex(r->roomId()); idx--; - if (idx > 0) { + if (idx >= 0) { setCurrentRoom( data(index(idx, 0), RoomlistModel::Roles::RoomId).toString()); } -- cgit 1.5.1 From 884fb74d2d1386c7355a709b3972619f5bd0f78a Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 18 Jun 2021 16:22:06 +0200 Subject: Add a basic 'Space page' --- resources/qml/ChatPage.qml | 2 +- resources/qml/RoomList.qml | 2 +- resources/qml/TimelineView.qml | 63 +++++++++++++++++++++++++++++++++++++++++- src/Cache.cpp | 7 +++++ src/Cache_p.h | 1 + src/timeline/TimelineModel.cpp | 8 ++++++ src/timeline/TimelineModel.h | 3 ++ 7 files changed, 83 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/resources/qml/ChatPage.qml b/resources/qml/ChatPage.qml index df2bf41f..cd323a97 100644 --- a/resources/qml/ChatPage.qml +++ b/resources/qml/ChatPage.qml @@ -71,7 +71,7 @@ Rectangle { AdaptiveLayoutElement { id: timlineViewC - minimumWidth: fontMetrics.averageCharacterWidth * 40 + Nheko.avatarSize + 2* Nheko.paddingMedium + minimumWidth: fontMetrics.averageCharacterWidth * 40 + Nheko.avatarSize + 2 * Nheko.paddingMedium TimelineView { id: timeline diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index 99e0ed41..d69f608b 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -254,9 +254,9 @@ Page { Label { id: timestamp + visible: !model.isInvite && !model.isSpace width: visible ? 0 : undefined - Layout.alignment: Qt.AlignRight | Qt.AlignBottom font.pixelSize: fontMetrics.font.pixelSize * 0.9 color: roomItem.unimportantText diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 90e28166..703f2fac 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -41,7 +41,8 @@ Item { ColumnLayout { id: timelineLayout - visible: room != null + visible: room != null && !room.isSpace + enabled: visible anchors.fill: parent spacing: 0 @@ -127,6 +128,66 @@ Item { } + ColumnLayout { + id: contentLayout1 + + visible: room != null && room.isSpace + enabled: visible + anchors.fill: parent + anchors.margins: Nheko.paddingLarge + spacing: Nheko.paddingLarge + + Avatar { + url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/") + displayName: room ? room.roomName : "" + height: 130 + width: 130 + Layout.alignment: Qt.AlignHCenter + enabled: false + } + + MatrixText { + text: room ? room.roomName : "" + font.pixelSize: 24 + Layout.alignment: Qt.AlignHCenter + } + + MatrixText { + text: qsTr("%1 member(s)").arg(room ? room.roomMemberCount : 0) + Layout.alignment: Qt.AlignHCenter + } + + ScrollView { + //Layout.maximumHeight: 75 + Layout.alignment: Qt.AlignHCenter + width: parent.width + + TextArea { + text: TimelineManager.escapeEmoji(room ? room.roomTopic : "") + wrapMode: TextEdit.WordWrap + textFormat: TextEdit.RichText + readOnly: true + background: null + selectByMouse: true + color: Nheko.colors.text + horizontalAlignment: TextEdit.AlignHCenter + onLinkActivated: Nheko.openLink(link) + + CursorShape { + anchors.fill: parent + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } + + } + + } + + Item { + Layout.fillHeight: true + } + + } + NhekoDropArea { anchors.fill: parent roomid: room ? room.roomId() : "" diff --git a/src/Cache.cpp b/src/Cache.cpp index 8b1798d6..144a2d9a 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -1772,6 +1772,13 @@ Cache::relatedEvents(const std::string &room_id, const std::string &event_id) return related_ids; } +size_t +Cache::memberCount(const std::string &room_id) +{ + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + return getMembersDb(txn, room_id).size(txn); +} + QMap Cache::roomInfo(bool withInvites) { diff --git a/src/Cache_p.h b/src/Cache_p.h index e35e78ec..cfcf9c9e 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -101,6 +101,7 @@ public: std::vector getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30); + size_t memberCount(const std::string &room_id); void saveState(const mtx::responses::Sync &res); bool isInitialized(); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 1ecb6cdf..13919e6d 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -774,6 +774,7 @@ TimelineModel::syncState(const mtx::responses::State &s) } else if (std::holds_alternative>(e)) { emit roomAvatarUrlChanged(); emit roomNameChanged(); + emit roomMemberCountChanged(); } } } @@ -830,6 +831,7 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline) } else if (std::holds_alternative>(e)) { emit roomAvatarUrlChanged(); emit roomNameChanged(); + emit roomMemberCountChanged(); } } updateLastMessage(); @@ -1935,3 +1937,9 @@ TimelineModel::roomTopic() const return utils::replaceEmoji(utils::linkifyMessage( QString::fromStdString(info[room_id_].topic).toHtmlEscaped())); } + +int +TimelineModel::roomMemberCount() const +{ + return (int)cache::client()->memberCount(room_id_.toStdString()); +} diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 42aa136f..3392d474 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -161,6 +161,7 @@ class TimelineModel : public QAbstractListModel Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged) Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY roomAvatarUrlChanged) Q_PROPERTY(QString roomTopic READ roomTopic NOTIFY roomTopicChanged) + Q_PROPERTY(int roomMemberCount READ roomMemberCount NOTIFY roomMemberCountChanged) Q_PROPERTY(bool isSpace READ isSpace CONSTANT) Q_PROPERTY(InputBar *input READ input CONSTANT) Q_PROPERTY(Permissions *permissions READ permissions NOTIFY permissionsChanged) @@ -264,6 +265,7 @@ public: DescInfo lastMessage() const { return lastMessage_; } bool isSpace() const { return isSpace_; } + int roomMemberCount() const; public slots: void setCurrentIndex(int index); @@ -350,6 +352,7 @@ signals: void roomNameChanged(); void roomTopicChanged(); void roomAvatarUrlChanged(); + void roomMemberCountChanged(); void permissionsChanged(); void forwardToRoom(mtx::events::collections::TimelineEvents *e, QString roomId); -- cgit 1.5.1 From d30446a8b39c70f87fad34b0c1958236c9f227cc Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 19 Jun 2021 01:46:23 +0200 Subject: Don't spam key requests directly after startup --- src/timeline/EventStore.cpp | 15 +++++++++++++++ src/timeline/EventStore.h | 2 ++ src/timeline/TimelineModel.cpp | 6 ++++++ 3 files changed, 23 insertions(+) (limited to 'src') diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 04f7ef76..9a91ff79 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -675,6 +675,9 @@ EventStore::decryptEvent(const IdIndex &idx, index.room_id, index.session_id, e.sender); + // we may not want to request keys during initial sync and such + if (suppressKeyRequests) + break; // TODO: Check if this actually works and look in key backup auto copy = e; copy.room_id = room_id_; @@ -816,6 +819,18 @@ EventStore::decryptEvent(const IdIndex &idx, return asCacheEntry(std::move(decryptionResult.event.value())); } +void +EventStore::enableKeyRequests(bool suppressKeyRequests_) +{ + if (!suppressKeyRequests_) { + for (const auto &key : decryptedEvents_.keys()) + if (key.room == this->room_id_) + decryptedEvents_.remove(key); + suppressKeyRequests = false; + } else + suppressKeyRequests = true; +} + mtx::events::collections::TimelineEvents * EventStore::get(std::string id, std::string_view related_to, bool decrypt, bool resolve_edits) { diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h index d9bb86cb..7c404102 100644 --- a/src/timeline/EventStore.h +++ b/src/timeline/EventStore.h @@ -115,6 +115,7 @@ public slots: void addPending(mtx::events::collections::TimelineEvents event); void receivedSessionKey(const std::string &session_id); void clearTimeline(); + void enableKeyRequests(bool suppressKeyRequests_); private: std::vector edits(const std::string &event_id); @@ -142,4 +143,5 @@ private: std::string current_txn; int current_txn_error_count = 0; bool noMoreMessages = false; + bool suppressKeyRequests = true; }; diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 13919e6d..067f219a 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -379,6 +379,7 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj connect(&events, &EventStore::updateFlowEventId, this, [this](std::string event_id) { this->updateFlowEventId(event_id); }); + // When a message is sent, check if the current edit/reply relates to that message, // and update the event_id so that it points to the sent message and not the pending one. connect(&events, @@ -395,6 +396,11 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj } }); + connect(manager_, + &TimelineViewManager::initialSyncChanged, + &events, + &EventStore::enableKeyRequests); + showEventTimer.callOnTimeout(this, &TimelineModel::scrollTimerEvent); } -- cgit 1.5.1 From f8dfc726255df415aaeb9339b20f83cd265ee303 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sun, 20 Jun 2021 13:30:35 +0200 Subject: Fix spaces showing up with world filter --- src/timeline/RoomlistModel.cpp | 69 +++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index a44959fc..7f59b112 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -531,6 +531,12 @@ bool FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) const { if (filterType == FilterBy::Nothing) { + if (sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) + .toBool()) { + return false; + } + if (!hiddenTags.empty()) { auto tags = sourceModel() @@ -540,7 +546,9 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons for (const auto &t : tags) if (hiddenTags.contains(t)) return false; - } else if (!hiddenSpaces.empty()) { + } + + if (!hiddenSpaces.empty()) { auto parents = sourceModel() ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::ParentSpaces) @@ -548,25 +556,30 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons for (const auto &t : parents) if (hiddenSpaces.contains(t)) return false; - } else if (sourceModel() - ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) - .toBool()) { - return false; } return true; } else if (filterType == FilterBy::Tag) { + if (sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) + .toBool()) { + return false; + } + auto tags = sourceModel() ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::Tags) .toStringList(); if (!tags.contains(filterStr)) return false; - else if (!hiddenTags.empty()) { + + if (!hiddenTags.empty()) { for (const auto &t : tags) if (t != filterStr && hiddenTags.contains(t)) return false; - } else if (!hiddenSpaces.empty()) { + } + + if (!hiddenSpaces.empty()) { auto parents = sourceModel() ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::ParentSpaces) @@ -574,41 +587,47 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons for (const auto &t : parents) if (hiddenSpaces.contains(t)) return false; - } else if (sourceModel() - ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) - .toBool()) { - return false; } + return true; } else if (filterType == FilterBy::Space) { + if (filterStr == sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::RoomId) + .toString()) + return true; + auto parents = sourceModel() ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::ParentSpaces) .toStringList(); - auto tags = sourceModel() - ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::Tags) - .toStringList(); - if (filterStr == sourceModel() - ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::RoomId) - .toString()) - return true; - else if (!parents.contains(filterStr)) + if (!parents.contains(filterStr)) return false; - else if (!hiddenTags.empty()) { + + if (!hiddenTags.empty()) { + auto tags = + sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::Tags) + .toStringList(); + for (const auto &t : tags) if (hiddenTags.contains(t)) return false; - } else if (!hiddenSpaces.empty()) { + } + + if (!hiddenSpaces.empty()) { for (const auto &t : parents) if (hiddenSpaces.contains(t)) return false; - } else if (sourceModel() - ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) - .toBool() && - !parents.contains(filterStr)) { + } + + if (sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) + .toBool() && + !parents.contains(filterStr)) { return false; } + return true; } else { return true; -- cgit 1.5.1 From 6a807894b40e28aeba865afddec7b61478b559d7 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Mon, 21 Jun 2021 01:44:05 +0200 Subject: Increase contrast of dark theme secondary colors slightly --- src/ui/Theme.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ui/Theme.cpp b/src/ui/Theme.cpp index 26119393..732a0443 100644 --- a/src/ui/Theme.cpp +++ b/src/ui/Theme.cpp @@ -47,7 +47,7 @@ Theme::paletteFromTheme(std::string_view theme) darkActive.setColor(QPalette::ToolTipBase, darkActive.base().color()); darkActive.setColor(QPalette::ToolTipText, darkActive.text().color()); darkActive.setColor(QPalette::Link, QColor("#38a3d8")); - darkActive.setColor(QPalette::ButtonText, "#727274"); + darkActive.setColor(QPalette::ButtonText, "#828284"); return darkActive; } else { return original; -- cgit 1.5.1 From 4a5414975d6b9c6502488e7c0d4c5674b81c1e3a Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 25 Jun 2021 11:26:39 +0200 Subject: fix sort by unread not getting persisted --- src/UserSettingsPage.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index 9b906555..6c0d8728 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -613,6 +613,7 @@ UserSettings::save() settings.setValue("mobile_mode", mobileMode_); settings.setValue("font_size", baseFontSize_); settings.setValue("typing_notifications", typingNotifications_); + settings.setValue("sort_by_unread", sortByImportance_); settings.setValue("minor_events", sortByImportance_); settings.setValue("read_receipts", readReceipts_); settings.setValue("group_view", groupView_); -- cgit 1.5.1