From 567fe81ad78e707a4b914976a92c855d4ac8fc45 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 14 May 2021 23:35:34 +0200 Subject: Basic header and footer of room list --- src/ui/Theme.cpp | 117 +++++++++++++++++++++++++------------------------------ 1 file changed, 53 insertions(+), 64 deletions(-) (limited to 'src/ui/Theme.cpp') diff --git a/src/ui/Theme.cpp b/src/ui/Theme.cpp index 4341bd63..ca2a4ce0 100644 --- a/src/ui/Theme.cpp +++ b/src/ui/Theme.cpp @@ -2,76 +2,65 @@ // // SPDX-License-Identifier: GPL-3.0-or-later -#include - #include "Theme.h" -Theme::Theme(QObject *parent) - : QObject(parent) -{ - setColor("Black", ui::Color::Black); - - setColor("BrightWhite", ui::Color::BrightWhite); - setColor("FadedWhite", ui::Color::FadedWhite); - setColor("MediumWhite", ui::Color::MediumWhite); - - setColor("BrightGreen", ui::Color::BrightGreen); - setColor("DarkGreen", ui::Color::DarkGreen); - setColor("LightGreen", ui::Color::LightGreen); - - setColor("Gray", ui::Color::Gray); - setColor("Red", ui::Color::Red); - setColor("Blue", ui::Color::Blue); - - setColor("Transparent", ui::Color::Transparent); -} - -QColor -Theme::rgba(int r, int g, int b, qreal a) const -{ - QColor color(r, g, b); - color.setAlphaF(a); - - return color; -} +Q_DECLARE_METATYPE(Theme) -QColor -Theme::getColor(const QString &key) const +QPalette +Theme::paletteFromTheme(std::string_view theme) { - if (!colors_.contains(key)) { - qWarning() << "Color with key" << key << "could not be found"; - return QColor(); + [[maybe_unused]] static auto meta = qRegisterMetaType("Theme"); + static QPalette original; + if (theme == "light") { + QPalette lightActive( + /*windowText*/ QColor("#333"), + /*button*/ QColor("white"), + /*light*/ QColor(0xef, 0xef, 0xef), + /*dark*/ QColor(110, 110, 110), + /*mid*/ QColor(220, 220, 220), + /*text*/ QColor("#333"), + /*bright_text*/ QColor("#333"), + /*base*/ QColor("#fff"), + /*window*/ QColor("white")); + lightActive.setColor(QPalette::AlternateBase, QColor("#eee")); + lightActive.setColor(QPalette::Highlight, QColor("#38a3d8")); + lightActive.setColor(QPalette::ToolTipBase, lightActive.base().color()); + lightActive.setColor(QPalette::ToolTipText, lightActive.text().color()); + lightActive.setColor(QPalette::Link, QColor("#0077b5")); + lightActive.setColor(QPalette::ButtonText, QColor("#555459")); + return lightActive; + } else if (theme == "dark") { + QPalette darkActive( + /*windowText*/ QColor("#caccd1"), + /*button*/ QColor(0xff, 0xff, 0xff), + /*light*/ QColor("#caccd1"), + /*dark*/ QColor(110, 110, 110), + /*mid*/ QColor("#202228"), + /*text*/ QColor("#caccd1"), + /*bright_text*/ QColor(0xff, 0xff, 0xff), + /*base*/ QColor("#202228"), + /*window*/ QColor("#2d3139")); + darkActive.setColor(QPalette::AlternateBase, QColor("#2d3139")); + darkActive.setColor(QPalette::Highlight, QColor("#38a3d8")); + 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"); + return darkActive; + } else { + return original; } - - return colors_.value(key); } -void -Theme::setColor(const QString &key, const QColor &color) +Theme::Theme(std::string_view theme) { - colors_.insert(key, color); -} - -void -Theme::setColor(const QString &key, ui::Color color) -{ - static const QColor palette[] = { - QColor("#171919"), - - QColor("#EBEBEB"), - QColor("#C9C9C9"), - QColor("#929292"), - - QColor("#1C3133"), - QColor("#577275"), - QColor("#46A451"), - - QColor("#5D6565"), - QColor("#E22826"), - QColor("#81B3A9"), - - rgba(0, 0, 0, 0), - }; - - colors_.insert(key, palette[static_cast(color)]); + auto p = paletteFromTheme(theme); + separator_ = p.mid().color(); + if (theme == "light") { + sidebarBackground_ = QColor("#233649"); + } else if (theme == "dark") { + sidebarBackground_ = QColor("#2d3139"); + } else { + sidebarBackground_ = p.window().color(); + } } -- cgit 1.5.1 From cd67046f6011f0838b5ed4621fb3ee9b846e63a0 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 21 May 2021 21:19:03 +0200 Subject: Make roomlist look nice --- resources/qml/RoomList.qml | 99 ++++++++++++++++++++++++++++++-------- src/CacheStructs.h | 13 +++++ src/ChatPage.cpp | 5 -- src/RoomList.cpp | 5 -- src/timeline/RoomlistModel.cpp | 107 ++++++++++++++++++++++++++++++++++++++--- src/timeline/RoomlistModel.h | 20 +++++++- src/timeline/TimelineModel.cpp | 30 ++++++++++-- src/timeline/TimelineModel.h | 14 ++++++ src/ui/Theme.cpp | 13 +++-- src/ui/Theme.h | 4 +- 10 files changed, 265 insertions(+), 45 deletions(-) (limited to 'src/ui/Theme.cpp') diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index 87a27517..bb8deda6 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -2,13 +2,15 @@ // // SPDX-License-Identifier: GPL-3.0-or-later -import QtQuick 2.9 +import QtQuick 2.13 import QtQuick.Controls 2.13 import QtQuick.Layouts 1.3 import im.nheko 1.0 Page { ListView { + id: roomlist + anchors.left: parent.left anchors.right: parent.right height: parent.height @@ -20,26 +22,80 @@ Page { enabled: !Settings.mobileMode } + Connections { + onActiveTimelineChanged: { + roomlist.positionViewAtIndex(Rooms.roomidToIndex(TimelineManager.timeline.roomId()), ListView.Contain); + console.log("Test" + TimelineManager.timeline.roomId() + " " + Rooms.roomidToIndex(TimelineManager.timeline.roomId)); + } + target: TimelineManager + } + delegate: Rectangle { - color: Nheko.colors.window - height: fontMetrics.lineSpacing * 2.5 + Nheko.paddingMedium * 2 + id: roomItem + + property color background: Nheko.colors.window + property color importantText: Nheko.colors.text + property color unimportantText: Nheko.colors.buttonText + property color bubbleBackground: Nheko.colors.highlight + property color bubbleText: Nheko.colors.highlightedText + + color: background + height: Math.ceil(fontMetrics.lineSpacing * 2.3 + Nheko.paddingMedium * 2) width: ListView.view.width + state: "normal" + states: [ + State { + name: "highlight" + when: hovered.hovered + + PropertyChanges { + target: roomItem + background: Nheko.colors.dark + importantText: Nheko.colors.brightText + unimportantText: Nheko.colors.brightText + bubbleBackground: Nheko.colors.highlight + bubbleText: Nheko.colors.highlightedText + } - RowLayout { - //id: userInfoGrid + }, + State { + name: "selected" + when: TimelineManager.timeline && model.roomId == TimelineManager.timeline.roomId() + + PropertyChanges { + target: roomItem + background: Nheko.colors.highlight + importantText: Nheko.colors.highlightedText + unimportantText: Nheko.colors.highlightedText + bubbleBackground: Nheko.colors.highlightedText + bubbleText: Nheko.colors.highlight + } + + } + ] + + HoverHandler { + id: hovered + } + TapHandler { + onSingleTapped: TimelineManager.setHistoryView(model.roomId) + } + + RowLayout { spacing: Nheko.paddingMedium anchors.fill: parent anchors.margins: Nheko.paddingMedium Avatar { + // In the future we could show an online indicator by setting the userid for the avatar //userid: Nheko.currentUser.userid id: avatar Layout.alignment: Qt.AlignVCenter - Layout.preferredWidth: fontMetrics.lineSpacing * 2.5 - Layout.preferredHeight: fontMetrics.lineSpacing * 2.5 + height: Math.ceil(fontMetrics.lineSpacing * 2.3) + width: Math.ceil(fontMetrics.lineSpacing * 2.3) url: model.avatarUrl.replace("mxc://", "image://MxcImage/") displayName: model.roomName } @@ -52,7 +108,7 @@ Page { Layout.minimumWidth: 100 width: parent.width - avatar.width Layout.preferredWidth: parent.width - avatar.width - spacing: 0 + spacing: Nheko.paddingSmall RowLayout { Layout.fillWidth: true @@ -60,9 +116,9 @@ Page { ElidedLabel { Layout.alignment: Qt.AlignBottom - color: Nheko.colors.text + color: roomItem.importantText elideWidth: textContent.width - timestamp.width - Nheko.paddingMedium - fullText: model.roomName + ": " + model.notificationCount + fullText: model.roomName } Item { @@ -74,8 +130,8 @@ Page { Layout.alignment: Qt.AlignRight | Qt.AlignBottom font.pixelSize: fontMetrics.font.pixelSize * 0.9 - color: Nheko.colors.buttonText - text: "14:32" + color: roomItem.unimportantText + text: model.timestamp } } @@ -85,10 +141,10 @@ Page { spacing: 0 ElidedLabel { - color: Nheko.colors.buttonText + color: roomItem.unimportantText font.weight: Font.Thin font.pixelSize: fontMetrics.font.pixelSize * 0.9 - elideWidth: textContent.width - notificationBubble.width + elideWidth: textContent.width - (notificationBubble.visible ? notificationBubble.width : 0) - Nheko.paddingSmall fullText: model.lastMessage } @@ -99,19 +155,24 @@ Page { Rectangle { id: notificationBubble + visible: model.notificationCount > 0 Layout.alignment: Qt.AlignRight - height: fontMetrics.font.pixelSize * 1.3 + height: fontMetrics.averageCharacterWidth * 3 width: height radius: height / 2 - color: Nheko.colors.highlight + color: model.hasLoudNotification ? Nheko.theme.red : roomItem.bubbleBackground Label { - anchors.fill: parent + anchors.centerIn: parent + width: parent.width * 0.8 + height: parent.height * 0.8 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter fontSizeMode: Text.Fit - color: Nheko.colors.highlightedText - text: model.notificationCount + font.bold: true + font.pixelSize: fontMetrics.font.pixelSize * 0.8 + color: model.hasLoudNotification ? "white" : roomItem.bubbleText + text: model.notificationCount > 99 ? "99+" : model.notificationCount } } diff --git a/src/CacheStructs.h b/src/CacheStructs.h index c449f013..f7d6f0e2 100644 --- a/src/CacheStructs.h +++ b/src/CacheStructs.h @@ -50,6 +50,19 @@ struct DescInfo QDateTime datetime; }; +inline bool +operator==(const DescInfo &a, const DescInfo &b) +{ + return std::tie(a.timestamp, a.event_id, a.userid, a.body, a.descriptiveTime) == + std::tie(b.timestamp, b.event_id, b.userid, b.body, b.descriptiveTime); +} +inline bool +operator!=(const DescInfo &a, const DescInfo &b) +{ + return std::tie(a.timestamp, a.event_id, a.userid, a.body, a.descriptiveTime) != + std::tie(b.timestamp, b.event_id, b.userid, b.body, b.descriptiveTime); +} + //! UI info associated with a room. struct RoomInfo { diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index c5199ff1..58b76174 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -233,11 +233,6 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) room_list_, &RoomList::updateRoomDescription); - connect(room_list_, - SIGNAL(totalUnreadMessageCountUpdated(int)), - this, - SIGNAL(unreadMessages(int))); - connect( this, &ChatPage::updateGroupsInfo, communitiesList_, &CommunitiesList::setCommunities); diff --git a/src/RoomList.cpp b/src/RoomList.cpp index 8a807e71..5c41a7a1 100644 --- a/src/RoomList.cpp +++ b/src/RoomList.cpp @@ -305,8 +305,6 @@ void RoomList::updateRoomAvatar(const QString &roomid, const QString &img) { if (!roomExists(roomid)) { - nhlog::ui()->warn("avatar update on non-existent room_id: {}", - roomid.toStdString()); return; } @@ -320,9 +318,6 @@ void RoomList::updateRoomDescription(const QString &roomid, const DescInfo &info) { if (!roomExists(roomid)) { - nhlog::ui()->warn("description update on non-existent room_id: {}, {}", - roomid.toStdString(), - info.body.toStdString()); return; } diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 6a1fc3c5..5fc4dc65 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -4,6 +4,7 @@ #include "RoomlistModel.h" +#include "Cache_p.h" #include "ChatPage.h" #include "MatrixClient.h" #include "MxcImageProvider.h" @@ -26,6 +27,11 @@ RoomlistModel::RoomlistModel(TimelineViewManager *parent) } } }); + + connect(this, + &RoomlistModel::totalUnreadMessageCountUpdated, + ChatPage::instance(), + &ChatPage::unreadMessages); } QHash @@ -34,8 +40,11 @@ RoomlistModel::roleNames() const return { {AvatarUrl, "avatarUrl"}, {RoomName, "roomName"}, + {RoomId, "roomId"}, {LastMessage, "lastMessage"}, + {Timestamp, "timestamp"}, {HasUnreadMessages, "hasUnreadMessages"}, + {HasLoudNotification, "hasLoudNotification"}, {NotificationCount, "notificationCount"}, }; } @@ -44,18 +53,26 @@ QVariant RoomlistModel::data(const QModelIndex &index, int role) const { if (index.row() >= 0 && static_cast(index.row()) < roomids.size()) { - auto room = models.value(roomids.at(index.row())); + auto roomid = roomids.at(index.row()); + auto room = models.value(roomid); switch (role) { case Roles::AvatarUrl: return room->roomAvatarUrl(); case Roles::RoomName: return room->roomName(); + case Roles::RoomId: + return room->roomId(); case Roles::LastMessage: - return QString("Nico: Hahaha, this is funny!"); + return room->lastMessage().body; + case Roles::Timestamp: + return room->lastMessage().descriptiveTime; case Roles::HasUnreadMessages: - return true; + return this->roomReadStatus.count(roomid) && + this->roomReadStatus.at(roomid); + case Roles::HasLoudNotification: + return room->hasMentions(); case Roles::NotificationCount: - return 5; + return room->notificationCount(); default: return {}; } @@ -64,10 +81,38 @@ RoomlistModel::data(const QModelIndex &index, int role) const } } +void +RoomlistModel::updateReadStatus(const std::map roomReadStatus_) +{ + std::vector roomsToUpdate; + roomsToUpdate.resize(roomReadStatus_.size()); + for (const auto &[roomid, roomUnread] : roomReadStatus_) { + if (roomUnread != roomReadStatus[roomid]) { + roomsToUpdate.push_back(this->roomidToIndex(roomid)); + } + } + + this->roomReadStatus = roomReadStatus_; + + for (auto idx : roomsToUpdate) { + emit dataChanged(index(idx), + index(idx), + { + Roles::HasUnreadMessages, + }); + } +}; void RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification) { if (!models.contains(room_id)) { + // ensure we get read status updates and are only connected once + connect(cache::client(), + &Cache::roomReadStatus, + this, + &RoomlistModel::updateReadStatus, + Qt::UniqueConnection); + QSharedPointer newRoom(new TimelineModel(manager, room_id)); newRoom->setDecryptDescription( ChatPage::instance()->userSettings()->decryptSidebar()); @@ -80,6 +125,56 @@ RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification) &TimelineModel::forwardToRoom, manager, &TimelineViewManager::forwardMessageToRoom); + connect( + newRoom.data(), &TimelineModel::lastMessageChanged, this, [room_id, this]() { + auto idx = this->roomidToIndex(room_id); + emit dataChanged(index(idx), + index(idx), + { + Roles::HasLoudNotification, + Roles::LastMessage, + Roles::Timestamp, + Roles::NotificationCount, + }); + }); + connect( + newRoom.data(), &TimelineModel::roomAvatarUrlChanged, this, [room_id, this]() { + auto idx = this->roomidToIndex(room_id); + emit dataChanged(index(idx), + index(idx), + { + Roles::AvatarUrl, + }); + }); + connect(newRoom.data(), &TimelineModel::roomNameChanged, this, [room_id, this]() { + auto idx = this->roomidToIndex(room_id); + emit dataChanged(index(idx), + index(idx), + { + Roles::RoomName, + }); + }); + connect( + newRoom.data(), &TimelineModel::notificationsChanged, this, [room_id, this]() { + auto idx = this->roomidToIndex(room_id); + emit dataChanged(index(idx), + index(idx), + { + Roles::HasLoudNotification, + Roles::NotificationCount, + }); + + int total_unread_msgs = 0; + + for (const auto &room : models) { + if (!room.isNull()) + total_unread_msgs += room->notificationCount(); + } + + emit totalUnreadMessageCountUpdated(total_unread_msgs); + }); + + newRoom->updateLastMessage(); if (!suppressInsertNotification) beginInsertRows(QModelIndex(), (int)roomids.size(), (int)roomids.size()); @@ -97,8 +192,8 @@ RoomlistModel::sync(const mtx::responses::Rooms &rooms) // addRoom will only add the room, if it doesn't exist addRoom(QString::fromStdString(room_id)); const auto &room_model = models.value(QString::fromStdString(room_id)); - room_model->syncState(room.state); - room_model->addEvents(room.timeline); + room_model->sync(room); + // room_model->addEvents(room.timeline); connect(room_model.data(), &TimelineModel::newCallEvent, manager->callManager(), diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h index 44fcf032..c4c9d9ba 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h @@ -22,8 +22,11 @@ public: { AvatarUrl = Qt::UserRole, RoomName, + RoomId, LastMessage, + Timestamp, HasUnreadMessages, + HasLoudNotification, NotificationCount, }; @@ -47,6 +50,21 @@ public slots: void initializeRooms(const std::vector &roomids); void sync(const mtx::responses::Rooms &rooms); void clear(); + int roomidToIndex(QString roomid) + { + for (int i = 0; i < (int)roomids.size(); i++) { + if (roomids[i] == roomid) + return i; + } + + return -1; + } + +private slots: + void updateReadStatus(const std::map roomReadStatus_); + +signals: + void totalUnreadMessageCountUpdated(int unreadMessages); private: void addRoom(const QString &room_id, bool suppressInsertNotification = false); @@ -54,5 +72,5 @@ private: TimelineViewManager *manager = nullptr; std::vector roomids; QHash> models; + std::map roomReadStatus; }; - diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 8df17457..19c3fb30 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -723,6 +723,20 @@ TimelineModel::fetchMore(const QModelIndex &) events.fetchMore(); } +void +TimelineModel::sync(const mtx::responses::JoinedRoom &room) +{ + this->syncState(room.state); + this->addEvents(room.timeline); + + if (room.unread_notifications.highlight_count != highlight_count || + room.unread_notifications.notification_count != notification_count) { + notification_count = room.unread_notifications.notification_count; + highlight_count = room.unread_notifications.highlight_count; + emit notificationsChanged(); + } +} + void TimelineModel::syncState(const mtx::responses::State &s) { @@ -866,14 +880,18 @@ TimelineModel::updateLastMessage() if (std::visit([](const auto &e) -> bool { return isYourJoin(e); }, *event)) { auto time = mtx::accessors::origin_server_ts(*event); uint64_t ts = time.toMSecsSinceEpoch(); - emit manager_->updateRoomsLastMessage( - room_id_, + auto description = DescInfo{QString::fromStdString(mtx::accessors::event_id(*event)), QString::fromStdString(http::client()->user_id().to_string()), tr("You joined this room."), utils::descriptiveTime(time), ts, - time}); + time}; + if (description != lastMessage_) { + lastMessage_ = description; + emit manager_->updateRoomsLastMessage(room_id_, lastMessage_); + emit lastMessageChanged(); + } return; } if (!std::visit([](const auto &e) -> bool { return isMessage(e); }, *event)) @@ -884,7 +902,11 @@ TimelineModel::updateLastMessage() QString::fromStdString(http::client()->user_id().to_string()), cache::displayName(room_id_, QString::fromStdString(mtx::accessors::sender(*event)))); - emit manager_->updateRoomsLastMessage(room_id_, description); + if (description != lastMessage_) { + lastMessage_ = description; + emit manager_->updateRoomsLastMessage(room_id_, description); + emit lastMessageChanged(); + } return; } } diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 92fccd2d..5c1065cb 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -14,6 +14,7 @@ #include #include "CacheCryptoStructs.h" +#include "CacheStructs.h" #include "EventStore.h" #include "InputBar.h" #include "Permissions.h" @@ -253,12 +254,15 @@ public: } void updateLastMessage(); + void sync(const mtx::responses::JoinedRoom &room); void addEvents(const mtx::responses::Timeline &events); void syncState(const mtx::responses::State &state); template void sendMessageEvent(const T &content, mtx::events::EventType eventType); RelatedInfo relatedInfo(QString id); + DescInfo lastMessage() const { return lastMessage_; } + public slots: void setCurrentIndex(int index); int currentIndex() const { return idToIndex(currentId); } @@ -309,6 +313,9 @@ public slots: QString roomAvatarUrl() const; QString roomId() const { return room_id_; } + bool hasMentions() { return highlight_count > 0; } + int notificationCount() { return notification_count; } + QString scrollTarget() const; private slots: @@ -328,6 +335,9 @@ signals: void newCallEvent(const mtx::events::collections::TimelineEvents &event); void scrollToIndex(int index); + void lastMessageChanged(); + void notificationsChanged(); + void openRoomSettingsDialog(RoomSettings *settings); void newMessageToSend(mtx::events::collections::TimelineEvents event); @@ -372,7 +382,11 @@ private: QString eventIdToShow; int showEventTimerCounter = 0; + DescInfo lastMessage_; + friend struct SendMessageVisitor; + + int notification_count = 0, highlight_count = 0; }; template diff --git a/src/ui/Theme.cpp b/src/ui/Theme.cpp index ca2a4ce0..b6c9579a 100644 --- a/src/ui/Theme.cpp +++ b/src/ui/Theme.cpp @@ -16,14 +16,15 @@ Theme::paletteFromTheme(std::string_view theme) /*windowText*/ QColor("#333"), /*button*/ QColor("white"), /*light*/ QColor(0xef, 0xef, 0xef), - /*dark*/ QColor(110, 110, 110), + /*dark*/ QColor(70, 77, 93), /*mid*/ QColor(220, 220, 220), /*text*/ QColor("#333"), - /*bright_text*/ QColor("#333"), + /*bright_text*/ QColor("#f2f5f8"), /*base*/ QColor("#fff"), /*window*/ QColor("white")); lightActive.setColor(QPalette::AlternateBase, QColor("#eee")); lightActive.setColor(QPalette::Highlight, QColor("#38a3d8")); + lightActive.setColor(QPalette::HighlightedText, QColor("#f4f4f5")); lightActive.setColor(QPalette::ToolTipBase, lightActive.base().color()); lightActive.setColor(QPalette::ToolTipText, lightActive.text().color()); lightActive.setColor(QPalette::Link, QColor("#0077b5")); @@ -34,14 +35,15 @@ Theme::paletteFromTheme(std::string_view theme) /*windowText*/ QColor("#caccd1"), /*button*/ QColor(0xff, 0xff, 0xff), /*light*/ QColor("#caccd1"), - /*dark*/ QColor(110, 110, 110), + /*dark*/ QColor(60, 70, 77), /*mid*/ QColor("#202228"), /*text*/ QColor("#caccd1"), - /*bright_text*/ QColor(0xff, 0xff, 0xff), + /*bright_text*/ QColor("#f4f5f8"), /*base*/ QColor("#202228"), /*window*/ QColor("#2d3139")); darkActive.setColor(QPalette::AlternateBase, QColor("#2d3139")); darkActive.setColor(QPalette::Highlight, QColor("#38a3d8")); + darkActive.setColor(QPalette::HighlightedText, QColor("#f4f5f8")); darkActive.setColor(QPalette::ToolTipBase, darkActive.base().color()); darkActive.setColor(QPalette::ToolTipText, darkActive.text().color()); darkActive.setColor(QPalette::Link, QColor("#38a3d8")); @@ -58,9 +60,12 @@ Theme::Theme(std::string_view theme) separator_ = p.mid().color(); if (theme == "light") { sidebarBackground_ = QColor("#233649"); + red_ = QColor("#a82353"); } else if (theme == "dark") { sidebarBackground_ = QColor("#2d3139"); + red_ = QColor("#a82353"); } else { sidebarBackground_ = p.window().color(); + red_ = QColor("red"); } } diff --git a/src/ui/Theme.h b/src/ui/Theme.h index 64bc8273..834571c0 100644 --- a/src/ui/Theme.h +++ b/src/ui/Theme.h @@ -66,6 +66,7 @@ class Theme : public QPalette Q_GADGET Q_PROPERTY(QColor sidebarBackground READ sidebarBackground CONSTANT) Q_PROPERTY(QColor separator READ separator CONSTANT) + Q_PROPERTY(QColor red READ red CONSTANT) public: Theme() {} explicit Theme(std::string_view theme); @@ -73,7 +74,8 @@ public: QColor sidebarBackground() const { return sidebarBackground_; } QColor separator() const { return separator_; } + QColor red() const { return red_; } private: - QColor sidebarBackground_, separator_; + QColor sidebarBackground_, separator_, red_; }; -- cgit 1.5.1 From c290b0747f34a6f683365f93d64ce93dc4428ca8 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Mon, 24 May 2021 14:04:07 +0200 Subject: Reenable invites --- resources/qml/RoomList.qml | 56 ++++++++++++ src/Cache.cpp | 50 +++++++++-- src/Cache.h | 2 +- src/Cache_p.h | 3 +- src/ChatPage.cpp | 4 +- src/ChatPage.h | 2 +- src/RoomList.cpp | 2 +- src/RoomList.h | 2 +- src/timeline/RoomlistModel.cpp | 169 ++++++++++++++++++++++++++++------- src/timeline/RoomlistModel.h | 8 +- src/timeline/TimelineViewManager.cpp | 4 +- src/timeline/TimelineViewManager.h | 2 +- src/ui/Theme.cpp | 3 + src/ui/Theme.h | 4 +- 14 files changed, 260 insertions(+), 51 deletions(-) (limited to 'src/ui/Theme.cpp') diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index 40669eda..e9bb351f 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -141,6 +141,8 @@ Page { RowLayout { Layout.fillWidth: true spacing: 0 + visible: !model.isInvite + height: visible ? 0 : undefined ElidedLabel { color: roomItem.unimportantText @@ -182,6 +184,60 @@ Page { } + RowLayout { + Layout.fillWidth: true + spacing: Nheko.paddingMedium + visible: model.isInvite + enabled: visible + height: visible ? 0 : undefined + + ElidedLabel { + elideWidth: textContent.width / 2 - 2 * Nheko.paddingMedium + fullText: qsTr("Accept") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + leftPadding: Nheko.paddingMedium + rightPadding: Nheko.paddingMedium + color: Nheko.colors.brightText + + TapHandler { + onSingleTapped: Rooms.acceptInvite(model.roomId) + } + + background: Rectangle { + color: Nheko.theme.alternateButton + radius: height / 2 + } + + } + + ElidedLabel { + Layout.alignment: Qt.AlignRight + elideWidth: textContent.width / 2 - 2 * Nheko.paddingMedium + fullText: qsTr("Decline") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + leftPadding: Nheko.paddingMedium + rightPadding: Nheko.paddingMedium + color: Nheko.colors.brightText + + TapHandler { + onSingleTapped: Rooms.declineInvite(model.roomId) + } + + background: Rectangle { + color: Nheko.theme.alternateButton + radius: height / 2 + } + + } + + Item { + Layout.fillWidth: true + } + + } + } } diff --git a/src/Cache.cpp b/src/Cache.cpp index c41b66cc..4a99dd59 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -2045,21 +2045,57 @@ Cache::getLastMessageInfo(lmdb::txn &txn, const std::string &room_id) return fallbackDesc; } -std::map +QHash Cache::invites() { - std::map result; + QHash result; auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto cursor = lmdb::cursor::open(txn, invitesDb_); - std::string_view room_id, unused; + std::string_view room_id, room_data; - while (cursor.get(room_id, unused, MDB_NEXT)) - result.emplace(QString::fromStdString(std::string(room_id)), true); + while (cursor.get(room_id, room_data, MDB_NEXT)) { + try { + RoomInfo tmp = json::parse(room_data); + tmp.member_count = getInviteMembersDb(txn, std::string(room_id)).size(txn); + result.insert(QString::fromStdString(std::string(room_id)), std::move(tmp)); + } catch (const json::exception &e) { + nhlog::db()->warn("failed to parse room info for invite: " + "room_id ({}), {}: {}", + room_id, + std::string(room_data), + e.what()); + } + } cursor.close(); - txn.commit(); + + return result; +} + +std::optional +Cache::invite(std::string_view roomid) +{ + std::optional result; + + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + + std::string_view room_data; + + if (invitesDb_.get(txn, roomid, room_data)) { + try { + RoomInfo tmp = json::parse(room_data); + tmp.member_count = getInviteMembersDb(txn, std::string(roomid)).size(txn); + result = std::move(tmp); + } catch (const json::exception &e) { + nhlog::db()->warn("failed to parse room info for invite: " + "room_id ({}), {}: {}", + roomid, + std::string(room_data), + e.what()); + } + } return result; } @@ -4064,7 +4100,7 @@ roomInfo(bool withInvites) { return instance_->roomInfo(withInvites); } -std::map +QHash invites() { return instance_->invites(); diff --git a/src/Cache.h b/src/Cache.h index 427dbafc..74ec9695 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -62,7 +62,7 @@ joinedRooms(); QMap roomInfo(bool withInvites = true); -std::map +QHash invites(); //! Calculate & return the name of the room. diff --git a/src/Cache_p.h b/src/Cache_p.h index c55fa601..f2911622 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -70,7 +70,8 @@ public: QMap roomInfo(bool withInvites = true); std::optional getRoomAliases(const std::string &roomid); - std::map invites(); + QHash invites(); + std::optional invite(std::string_view roomid); //! Calculate & return the name of the room. QString getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 58b76174..166c03ec 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -313,7 +313,7 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect(this, &ChatPage::initializeEmptyViews, view_manager_, - &TimelineViewManager::initWithMessages); + &TimelineViewManager::initializeRoomlist); connect(this, &ChatPage::initializeMentions, user_mentions_popup_, @@ -554,7 +554,7 @@ ChatPage::loadStateFromCache() try { olm::client()->load(cache::restoreOlmAccount(), STORAGE_SECRET_KEY); - emit initializeEmptyViews(cache::client()->roomIds()); + emit initializeEmptyViews(); emit initializeRoomList(cache::roomInfo()); emit initializeMentions(cache::getTimelineMentions()); emit syncTags(cache::roomInfo().toStdMap()); diff --git a/src/ChatPage.h b/src/ChatPage.h index 84e7cdff..eb60047d 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -147,7 +147,7 @@ signals: void initializeRoomList(QMap); void initializeViews(const mtx::responses::Rooms &rooms); - void initializeEmptyViews(const std::vector &roomIds); + void initializeEmptyViews(); void initializeMentions(const QMap ¬ifs); void syncUI(const mtx::responses::Rooms &rooms); void syncRoomlist(const std::map &updates); diff --git a/src/RoomList.cpp b/src/RoomList.cpp index 5c41a7a1..5839c4a0 100644 --- a/src/RoomList.cpp +++ b/src/RoomList.cpp @@ -183,7 +183,7 @@ RoomList::initialize(const QMap &info) } void -RoomList::cleanupInvites(const std::map &invites) +RoomList::cleanupInvites(const QHash &invites) { if (invites.size() == 0) return; diff --git a/src/RoomList.h b/src/RoomList.h index 74152c55..af792fd7 100644 --- a/src/RoomList.h +++ b/src/RoomList.h @@ -48,7 +48,7 @@ public: //! Show all the available rooms. void removeFilter(const std::set &roomsToHide); void updateRoom(const QString &room_id, const RoomInfo &info); - void cleanupInvites(const std::map &invites); + void cleanupInvites(const QHash &invites); signals: void roomChanged(const QString &room_id); diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 28c3cf46..f3d4dad7 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -57,31 +57,64 @@ RoomlistModel::data(const QModelIndex &index, int role) const { if (index.row() >= 0 && static_cast(index.row()) < roomids.size()) { auto roomid = roomids.at(index.row()); - auto room = models.value(roomid); - switch (role) { - case Roles::AvatarUrl: - return room->roomAvatarUrl(); - case Roles::RoomName: - return room->plainRoomName(); - case Roles::RoomId: - return room->roomId(); - case Roles::LastMessage: - return room->lastMessage().body; - case Roles::Time: - return room->lastMessage().descriptiveTime; - case Roles::Timestamp: - return QVariant(static_cast(room->lastMessage().timestamp)); - case Roles::HasUnreadMessages: - return this->roomReadStatus.count(roomid) && - this->roomReadStatus.at(roomid); - case Roles::HasLoudNotification: - return room->hasMentions(); - case Roles::NotificationCount: - return room->notificationCount(); - case Roles::IsInvite: - case Roles::IsSpace: - return false; - default: + + if (models.contains(roomid)) { + auto room = models.value(roomid); + switch (role) { + case Roles::AvatarUrl: + return room->roomAvatarUrl(); + case Roles::RoomName: + return room->plainRoomName(); + case Roles::RoomId: + return room->roomId(); + case Roles::LastMessage: + return room->lastMessage().body; + case Roles::Time: + return room->lastMessage().descriptiveTime; + case Roles::Timestamp: + return QVariant( + static_cast(room->lastMessage().timestamp)); + case Roles::HasUnreadMessages: + return this->roomReadStatus.count(roomid) && + this->roomReadStatus.at(roomid); + case Roles::HasLoudNotification: + return room->hasMentions(); + case Roles::NotificationCount: + return room->notificationCount(); + case Roles::IsInvite: + case Roles::IsSpace: + return false; + default: + return {}; + } + } else if (invites.contains(roomid)) { + auto room = invites.value(roomid); + switch (role) { + case Roles::AvatarUrl: + return QString::fromStdString(room.avatar_url); + case Roles::RoomName: + return QString::fromStdString(room.name); + case Roles::RoomId: + return roomid; + case Roles::LastMessage: + return room.msgInfo.body; + case Roles::Time: + return room.msgInfo.descriptiveTime; + case Roles::Timestamp: + return QVariant(static_cast(room.msgInfo.timestamp)); + case Roles::HasUnreadMessages: + case Roles::HasLoudNotification: + return false; + case Roles::NotificationCount: + return 0; + case Roles::IsInvite: + return true; + case Roles::IsSpace: + return false; + default: + return {}; + } + } else { return {}; } } else { @@ -109,7 +142,7 @@ RoomlistModel::updateReadStatus(const std::map roomReadStatus_) Roles::HasUnreadMessages, }); } -}; +} void RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification) { @@ -186,11 +219,21 @@ RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification) newRoom->updateLastMessage(); - if (!suppressInsertNotification) + bool wasInvite = invites.contains(room_id); + if (!suppressInsertNotification && !wasInvite) beginInsertRows(QModelIndex(), (int)roomids.size(), (int)roomids.size()); + models.insert(room_id, std::move(newRoom)); - roomids.push_back(room_id); - if (!suppressInsertNotification) + + if (wasInvite) { + auto idx = roomidToIndex(room_id); + invites.remove(room_id); + emit dataChanged(index(idx), index(idx)); + } else { + roomids.push_back(room_id); + } + + if (!suppressInsertNotification && !wasInvite) endInsertRows(); } } @@ -234,20 +277,50 @@ RoomlistModel::sync(const mtx::responses::Rooms &rooms) if (idx != -1) { beginRemoveRows(QModelIndex(), idx, idx); roomids.erase(roomids.begin() + idx); - models.remove(QString::fromStdString(room_id)); + if (models.contains(QString::fromStdString(room_id))) + models.remove(QString::fromStdString(room_id)); + else if (invites.contains(QString::fromStdString(room_id))) + invites.remove(QString::fromStdString(room_id)); endRemoveRows(); } } + + for (const auto &[room_id, room] : rooms.invite) { + (void)room_id; + auto qroomid = QString::fromStdString(room_id); + + auto invite = cache::client()->invite(room_id); + if (!invite) + continue; + + if (invites.contains(qroomid)) { + invites[qroomid] = *invite; + auto idx = roomidToIndex(qroomid); + emit dataChanged(index(idx), index(idx)); + } else { + beginInsertRows(QModelIndex(), (int)roomids.size(), (int)roomids.size()); + invites.insert(qroomid, *invite); + roomids.push_back(std::move(qroomid)); + endInsertRows(); + } + } } void -RoomlistModel::initializeRooms(const std::vector &roomIds_) +RoomlistModel::initializeRooms() { beginResetModel(); models.clear(); roomids.clear(); - for (const auto &id : roomIds_) + invites.clear(); + + invites = cache::client()->invites(); + for (const auto &id : invites.keys()) + roomids.push_back(id); + + for (const auto &id : cache::client()->roomIds()) addRoom(id, true); + endResetModel(); } @@ -256,10 +329,42 @@ RoomlistModel::clear() { beginResetModel(); models.clear(); + invites.clear(); roomids.clear(); endResetModel(); } +void +RoomlistModel::acceptInvite(QString roomid) +{ + if (invites.contains(roomid)) { + auto idx = roomidToIndex(roomid); + + if (idx != -1) { + beginRemoveRows(QModelIndex(), idx, idx); + roomids.erase(roomids.begin() + idx); + invites.remove(roomid); + endRemoveRows(); + ChatPage::instance()->joinRoom(roomid); + } + } +} +void +RoomlistModel::declineInvite(QString roomid) +{ + if (invites.contains(roomid)) { + auto idx = roomidToIndex(roomid); + + if (idx != -1) { + beginRemoveRows(QModelIndex(), idx, idx); + roomids.erase(roomids.begin() + idx); + invites.remove(roomid); + endRemoveRows(); + ChatPage::instance()->leaveRoom(roomid); + } + } +} + namespace { enum NotificationImportance : short { diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h index c3374bd2..ff85614c 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -51,7 +52,7 @@ public: } public slots: - void initializeRooms(const std::vector &roomids); + void initializeRooms(); void sync(const mtx::responses::Rooms &rooms); void clear(); int roomidToIndex(QString roomid) @@ -63,6 +64,8 @@ public slots: return -1; } + void acceptInvite(QString roomid); + void declineInvite(QString roomid); private slots: void updateReadStatus(const std::map roomReadStatus_); @@ -75,6 +78,7 @@ private: TimelineViewManager *manager = nullptr; std::vector roomids; + QHash invites; QHash> models; std::map roomReadStatus; @@ -94,6 +98,8 @@ public slots: return mapFromSource(roomlistmodel->index(roomlistmodel->roomidToIndex(roomid))) .row(); } + void acceptInvite(QString roomid) { roomlistmodel->acceptInvite(roomid); } + void declineInvite(QString roomid) { roomlistmodel->declineInvite(roomid); } private: short int calculateImportance(const QModelIndex &idx) const; diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index c84e0df8..9fa7f8b6 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -499,9 +499,9 @@ TimelineViewManager::receivedSessionKey(const std::string &room_id, const std::s } void -TimelineViewManager::initWithMessages(const std::vector &roomIds) +TimelineViewManager::initializeRoomlist() { - rooms->initializeRooms(roomIds); + rooms->initializeRooms(); } void diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 609f5a4a..37e50804 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -100,7 +100,7 @@ signals: public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); void receivedSessionKey(const std::string &room_id, const std::string &session_id); - void initWithMessages(const std::vector &roomIds); + void initializeRoomlist(); void chatFocusChanged(bool focused) { isWindowFocused_ = focused; diff --git a/src/ui/Theme.cpp b/src/ui/Theme.cpp index b6c9579a..26119393 100644 --- a/src/ui/Theme.cpp +++ b/src/ui/Theme.cpp @@ -60,12 +60,15 @@ Theme::Theme(std::string_view theme) separator_ = p.mid().color(); if (theme == "light") { sidebarBackground_ = QColor("#233649"); + alternateButton_ = QColor("#ccc"); red_ = QColor("#a82353"); } else if (theme == "dark") { sidebarBackground_ = QColor("#2d3139"); + alternateButton_ = QColor("#414A59"); red_ = QColor("#a82353"); } else { sidebarBackground_ = p.window().color(); + alternateButton_ = p.dark().color(); red_ = QColor("red"); } } diff --git a/src/ui/Theme.h b/src/ui/Theme.h index 834571c0..b5bcd4dd 100644 --- a/src/ui/Theme.h +++ b/src/ui/Theme.h @@ -65,6 +65,7 @@ class Theme : public QPalette { Q_GADGET Q_PROPERTY(QColor sidebarBackground READ sidebarBackground CONSTANT) + Q_PROPERTY(QColor alternateButton READ alternateButton CONSTANT) Q_PROPERTY(QColor separator READ separator CONSTANT) Q_PROPERTY(QColor red READ red CONSTANT) public: @@ -73,9 +74,10 @@ public: static QPalette paletteFromTheme(std::string_view theme); QColor sidebarBackground() const { return sidebarBackground_; } + QColor alternateButton() const { return alternateButton_; } QColor separator() const { return separator_; } QColor red() const { return red_; } private: - QColor sidebarBackground_, separator_, red_; + QColor sidebarBackground_, separator_, red_, alternateButton_; }; -- cgit 1.5.1