diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a5e3ba1..3d9d793c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -265,6 +265,7 @@ set(SRC_FILES # Timeline + src/timeline/CommunitiesModel.cpp src/timeline/EventStore.cpp src/timeline/InputBar.cpp src/timeline/Reaction.cpp @@ -481,6 +482,7 @@ qt5_wrap_cpp(MOC_HEADERS src/emoji/Provider.h # Timeline + src/timeline/CommunitiesModel.h src/timeline/EventStore.h src/timeline/InputBar.h src/timeline/Reaction.h diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index 21973b77..a6637467 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -43,12 +43,10 @@ Page { property string roomid property var tags - property var allTags function show(roomid_, tags_) { roomid = roomid_; tags = tags_; - allTags = Rooms.tags(); open(); } @@ -72,7 +70,7 @@ Page { } Instantiator { - model: roomContextMenu.allTags + model: Communities.tags onObjectAdded: roomContextMenu.insertItem(index + 2, object) onObjectRemoved: roomContextMenu.removeItem(object) @@ -92,7 +90,7 @@ Page { } } checkable: true - checked: roomContextMenu.tags.includes(t) + checked: roomContextMenu.tags !== undefined && roomContextMenu.tags.includes(t) onTriggered: Rooms.toggleTag(roomContextMenu.roomid, t, checked) } diff --git a/src/timeline/CommunitiesModel.cpp b/src/timeline/CommunitiesModel.cpp new file mode 100644 index 00000000..cedaacce --- /dev/null +++ b/src/timeline/CommunitiesModel.cpp @@ -0,0 +1,158 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "CommunitiesModel.h" + +#include + +#include "Cache.h" +#include "UserSettingsPage.h" + +CommunitiesModel::CommunitiesModel(QObject *parent) + : QAbstractListModel(parent) +{} + +QHash +CommunitiesModel::roleNames() const +{ + return { + {AvatarUrl, "avatarUrl"}, + {DisplayName, "displayName"}, + {Tooltip, "tooltip"}, + {ChildrenHidden, "childrenHidden"}, + }; +} + +QVariant +CommunitiesModel::data(const QModelIndex &index, int role) const +{ + if (index.row() == 0) { + switch (role) { + case CommunitiesModel::Roles::AvatarUrl: + return QString(":/icons/icons/ui/world.png"); + case CommunitiesModel::Roles::DisplayName: + return tr("All rooms"); + case CommunitiesModel::Roles::Tooltip: + return tr("Shows all rooms without filtering."); + case CommunitiesModel::Roles::ChildrenHidden: + return false; + case CommunitiesModel::Roles::Id: + return ""; + } + } else if (index.row() - 1 < tags_.size()) { + auto tag = tags_.at(index.row() - 1); + if (tag == "m.favourite") { + switch (role) { + case CommunitiesModel::Roles::AvatarUrl: + return QString(":/icons/icons/ui/star.png"); + case CommunitiesModel::Roles::DisplayName: + return tr("Favourites"); + case CommunitiesModel::Roles::Tooltip: + return tr("Rooms you have favourited."); + } + } else if (tag == "m.lowpriority") { + switch (role) { + case CommunitiesModel::Roles::AvatarUrl: + return QString(":/icons/icons/ui/star.png"); + case CommunitiesModel::Roles::DisplayName: + return tr("Low Priority"); + case CommunitiesModel::Roles::Tooltip: + return tr("Rooms with low priority."); + } + } else if (tag == "m.server_notice") { + switch (role) { + case CommunitiesModel::Roles::AvatarUrl: + return QString(":/icons/icons/ui/tag.png"); + case CommunitiesModel::Roles::DisplayName: + return tr("Server Notices"); + case CommunitiesModel::Roles::Tooltip: + return tr("Messages from your server or administrator."); + } + } else { + switch (role) { + case CommunitiesModel::Roles::AvatarUrl: + return QString(":/icons/icons/ui/tag.png"); + case CommunitiesModel::Roles::DisplayName: + return tag.right(2); + case CommunitiesModel::Roles::Tooltip: + return tag.right(2); + } + } + + switch (role) { + case CommunitiesModel::Roles::ChildrenHidden: + return UserSettings::instance()->hiddenTags().contains("tag:" + tag); + case CommunitiesModel::Roles::Id: + return "tag:" + tag; + } + } + return QVariant(); +} + +void +CommunitiesModel::initializeSidebar() +{ + 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); + } + } + } + + beginResetModel(); + tags_.clear(); + for (const auto &t : ts) + tags_.push_back(QString::fromStdString(t)); + endResetModel(); + + emit tagsChanged(); +} + +void +CommunitiesModel::clear() +{ + beginResetModel(); + tags_.clear(); + endResetModel(); + + emit tagsChanged(); +} + +void +CommunitiesModel::sync(const mtx::responses::Rooms &rooms) +{ + bool tagsUpdated = false; + + for (const auto &[roomid, room] : rooms.join) { + (void)roomid; + for (const auto &e : room.account_data.events) + if (std::holds_alternative< + mtx::events::AccountDataEvent>(e)) { + tagsUpdated = true; + } + } + + if (tagsUpdated) + initializeSidebar(); +} + +void +CommunitiesModel::setCurrentTagId(QString tagId) +{ + if (tagId.startsWith("tag:")) { + auto tag = tagId.remove(0, 4); + for (const auto &t : tags_) { + if (t == tag) { + this->currentTagId_ = tagId; + emit currentTagIdChanged(); + return; + } + } + } + + this->currentTagId_ = ""; + emit currentTagIdChanged(); +} diff --git a/src/timeline/CommunitiesModel.h b/src/timeline/CommunitiesModel.h new file mode 100644 index 00000000..3f6a2a4c --- /dev/null +++ b/src/timeline/CommunitiesModel.h @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include + +class CommunitiesModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(QString currentTagId READ currentTagId WRITE setCurrentTagId NOTIFY + currentTagIdChanged RESET resetCurrentTagId) + Q_PROPERTY(QStringList tags READ tags NOTIFY tagsChanged) + +public: + enum Roles + { + AvatarUrl = Qt::UserRole, + DisplayName, + Tooltip, + ChildrenHidden, + Id, + }; + + CommunitiesModel(QObject *parent = nullptr); + QHash roleNames() const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override + { + (void)parent; + return 1 + tags_.size(); + } + QVariant data(const QModelIndex &index, int role) const override; + +public slots: + void initializeSidebar(); + void sync(const mtx::responses::Rooms &rooms); + void clear(); + QString currentTagId() const { return currentTagId_; } + void setCurrentTagId(QString tagId); + void resetCurrentTagId() + { + currentTagId_.clear(); + emit currentTagIdChanged(); + } + QStringList tags() const { return tags_; } + +signals: + void currentTagIdChanged(); + void tagsChanged(); + +private: + QStringList tags_; + QString currentTagId_; +}; diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 283224f1..4dd44b30 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -485,29 +485,6 @@ FilteredRoomlistModel::FilteredRoomlistModel(RoomlistModel *model, QObject *pare sort(0); } -QStringList -FilteredRoomlistModel::tags() -{ - std::set ts; - for (const auto &e : cache::roomInfo()) { - for (const auto &t : e.tags) { - if (t.find("u.") == 0) { - ts.insert(t); - } - } - } - - QStringList ret{{ - "m.favourite", - "m.lowpriority", - }}; - - for (const auto &t : ts) - ret.push_back(QString::fromStdString(t)); - - return ret; -} - void FilteredRoomlistModel::toggleTag(QString roomid, QString tag, bool on) { diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h index fa991f6b..7ee0419f 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h @@ -119,7 +119,6 @@ public slots: void acceptInvite(QString roomid) { roomlistmodel->acceptInvite(roomid); } void declineInvite(QString roomid) { roomlistmodel->declineInvite(roomid); } void leave(QString roomid) { roomlistmodel->leave(roomid); } - QStringList tags(); void toggleTag(QString roomid, QString tag, bool on); TimelineModel *currentRoom() const { return roomlistmodel->currentRoom(); } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index dd623f2f..faf56b85 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -135,6 +135,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par , blurhashProvider(new BlurhashProvider()) , callManager_(callManager) , rooms_(new RoomlistModel(this)) + , communities_(new CommunitiesModel(this)) { qRegisterMetaType(); qRegisterMetaType(); @@ -196,6 +197,12 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par "im.nheko", 1, 0, "Rooms", [](QQmlEngine *, QJSEngine *) -> QObject * { return new FilteredRoomlistModel(self->rooms_); }); + qmlRegisterSingletonType( + "im.nheko", 1, 0, "Communities", [](QQmlEngine *, QJSEngine *) -> QObject * { + auto ptr = self->communities_; + QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership); + return ptr; + }); qmlRegisterSingletonType( "im.nheko", 1, 0, "Settings", [](QQmlEngine *, QJSEngine *) -> QObject * { auto ptr = ChatPage::instance()->userSettings().data(); @@ -324,6 +331,7 @@ void TimelineViewManager::sync(const mtx::responses::Rooms &rooms_res) { this->rooms_->sync(rooms_res); + this->communities_->sync(rooms_res); if (isInitialSync_) { this->isInitialSync_ = false; @@ -486,6 +494,7 @@ void TimelineViewManager::initializeRoomlist() { rooms_->initializeRooms(); + communities_->initializeSidebar(); } void diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 68d9cd1b..556bcf4c 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -22,6 +22,7 @@ #include "WebRTCSession.h" #include "emoji/EmojiModel.h" #include "emoji/Provider.h" +#include "timeline/CommunitiesModel.h" #include "timeline/RoomlistModel.h" class MxcImageProvider; @@ -131,7 +132,8 @@ private: bool isInitialSync_ = true; bool isWindowFocused_ = false; - RoomlistModel *rooms_ = nullptr; + RoomlistModel *rooms_ = nullptr; + CommunitiesModel *communities_ = nullptr; QHash userColors;