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/Cache.cpp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'src/Cache.cpp') diff --git a/src/Cache.cpp b/src/Cache.cpp index 24b2bc24..d8c78381 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -253,6 +253,8 @@ Cache::setup() outboundMegolmSessionDb_ = lmdb::dbi::open(txn, OUTBOUND_MEGOLM_SESSIONS_DB, MDB_CREATE); txn.commit(); + + databaseReady_ = true; } void @@ -788,6 +790,7 @@ Cache::nextBatchToken() void Cache::deleteData() { + this->databaseReady_ = false; // TODO: We need to remove the env_ while not accepting new requests. lmdb::dbi_close(env_, syncStateDb_); lmdb::dbi_close(env_, roomsDb_); @@ -2426,7 +2429,7 @@ Cache::joinedRooms() std::optional Cache::getMember(const std::string &room_id, const std::string &user_id) { - if (user_id.empty()) + if (user_id.empty() || !env_.handle()) return std::nullopt; try { @@ -3551,8 +3554,8 @@ Cache::query_keys(const std::string &user_id, http::client()->query_keys( req, - [cb, user_id, last_changed](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { + [cb, user_id, last_changed, this](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {},{}", mtx::errors::to_string(err->matrix_error.errcode), @@ -3561,10 +3564,22 @@ Cache::query_keys(const std::string &user_id, return; } - cache::updateUserKeys(last_changed, res); - - auto keys = cache::userKeys(user_id); - cb(keys.value_or(UserKeyCache{}), err); + emit userKeysUpdate(last_changed, res); + + // use context object so that we can disconnect again + std::unique_ptr context{new QObject}; + QObject *pcontext = context.get(); + QObject::connect( + this, + &Cache::verificationStatusChanged, + pcontext, + [cb, user_id, context_ = std::move(context)](std::string updated_user) mutable { + if (user_id == updated_user) { + context_.release(); + auto keys = cache::userKeys(user_id); + cb(keys.value_or(UserKeyCache{}), {}); + } + }); }); } @@ -3999,6 +4014,8 @@ avatarUrl(const QString &room_id, const QString &user_id) mtx::presence::PresenceState presenceState(const std::string &user_id) { + if (!instance_) + return {}; return instance_->presenceState(user_id); } std::string -- cgit 1.5.1 From f2bc1845508cc2d90540404b6b9e11192cc56104 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 22 May 2021 14:31:38 +0200 Subject: Fix device list not showing up and UserProfile blocking the window --- resources/qml/RoomList.qml | 10 +++++++ resources/qml/RoomSettings.qml | 4 +-- resources/qml/UserProfile.qml | 2 +- .../qml/device-verification/DeviceVerification.qml | 2 +- src/Cache.cpp | 32 ++++++++++------------ src/ui/NhekoGlobalObject.h | 2 +- src/ui/UserProfile.cpp | 4 ++- src/ui/UserProfile.h | 3 +- 8 files changed, 35 insertions(+), 24 deletions(-) (limited to 'src/Cache.cpp') diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index 979e727d..cde744c5 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -212,6 +212,16 @@ Page { Layout.preferredHeight: userInfoGrid.implicitHeight + 2 * Nheko.paddingMedium Layout.minimumHeight: 40 + TapHandler { + onSingleTapped: { + Nheko.updateUserProfile(); + var userProfile = userProfileComponent.createObject(timelineRoot, { + "profile": Nheko.currentUser + }); + userProfile.show(); + } + } + RowLayout { id: userInfoGrid diff --git a/resources/qml/RoomSettings.qml b/resources/qml/RoomSettings.qml index 14de0edf..1f7fe5de 100644 --- a/resources/qml/RoomSettings.qml +++ b/resources/qml/RoomSettings.qml @@ -20,7 +20,7 @@ ApplicationWindow { minimumHeight: 650 palette: Nheko.colors color: Nheko.colors.window - modality: Qt.WindowModal + modality: Qt.NonModal flags: Qt.Dialog title: qsTr("Room Settings") @@ -205,7 +205,7 @@ ApplicationWindow { title: qsTr("End-to-End Encryption") text: qsTr("Encryption is currently experimental and things might break unexpectedly.
Please take note that it can't be disabled afterwards.") - modality: Qt.WindowModal + modality: Qt.NonModal onAccepted: { if (roomSettings.isEncryptionEnabled) return ; diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 4e5e64dc..21f34f15 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -22,7 +22,7 @@ ApplicationWindow { palette: Nheko.colors color: Nheko.colors.window title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile") - modality: Qt.WindowModal + modality: Qt.NonModal flags: Qt.Dialog Shortcut { diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 6d0be204..e2c66c5a 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -15,7 +15,7 @@ ApplicationWindow { onClosing: TimelineManager.removeVerificationFlow(flow) title: stack.currentItem.title flags: Qt.Dialog - modality: Qt.WindowModal + modality: Qt.NonModal palette: Nheko.colors height: stack.implicitHeight width: stack.implicitWidth diff --git a/src/Cache.cpp b/src/Cache.cpp index d8c78381..c41b66cc 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -3469,6 +3469,7 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query } } } + for (auto &[user_id, update] : updates) { (void)update; if (user_id == local_user) { @@ -3476,9 +3477,8 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query (void)status; emit verificationStatusChanged(user); } - } else { - emit verificationStatusChanged(user_id); } + emit verificationStatusChanged(user_id); } } @@ -3552,6 +3552,19 @@ Cache::query_keys(const std::string &user_id, last_changed = cache_->last_changed; req.token = last_changed; + // use context object so that we can disconnect again + QObject *context{new QObject(this)}; + QObject::connect(this, + &Cache::verificationStatusChanged, + context, + [cb, user_id, context_ = context](std::string updated_user) mutable { + if (user_id == updated_user) { + context_->deleteLater(); + auto keys = cache::userKeys(user_id); + cb(keys.value_or(UserKeyCache{}), {}); + } + }); + http::client()->query_keys( req, [cb, user_id, last_changed, this](const mtx::responses::QueryKeys &res, @@ -3565,21 +3578,6 @@ Cache::query_keys(const std::string &user_id, } emit userKeysUpdate(last_changed, res); - - // use context object so that we can disconnect again - std::unique_ptr context{new QObject}; - QObject *pcontext = context.get(); - QObject::connect( - this, - &Cache::verificationStatusChanged, - pcontext, - [cb, user_id, context_ = std::move(context)](std::string updated_user) mutable { - if (user_id == updated_user) { - context_.release(); - auto keys = cache::userKeys(user_id); - cb(keys.value_or(UserKeyCache{}), {}); - } - }); }); } diff --git a/src/ui/NhekoGlobalObject.h b/src/ui/NhekoGlobalObject.h index fe645a34..fc35fe22 100644 --- a/src/ui/NhekoGlobalObject.h +++ b/src/ui/NhekoGlobalObject.h @@ -40,7 +40,7 @@ public: Q_INVOKABLE void openLink(QString link) const; -private slots: +public slots: void updateUserProfile(); signals: diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index cef8bd85..da130242 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -42,7 +42,6 @@ UserProfile::UserProfile(QString roomid, if (!cache::client() || !cache::client()->isDatabaseReady()) return; - fetchDeviceList(this->userid_); connect(cache::client(), &Cache::verificationStatusChanged, this, @@ -66,7 +65,9 @@ UserProfile::UserProfile(QString roomid, : verification::VERIFIED; } deviceList_.reset(deviceList_.deviceList_); + emit devicesChanged(); }); + fetchDeviceList(this->userid_); } QHash @@ -223,6 +224,7 @@ UserProfile::fetchDeviceList(const QString &userID) } this->deviceList_.queueReset(std::move(deviceInfo)); + emit devicesChanged(); }); }); } diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index bf71d0de..721d7230 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -90,7 +90,7 @@ class UserProfile : public QObject Q_PROPERTY(QString displayName READ displayName NOTIFY displayNameChanged) Q_PROPERTY(QString userid READ userid CONSTANT) Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged) - Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT) + Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList NOTIFY devicesChanged) Q_PROPERTY(bool isGlobalUserProfile READ isGlobalUserProfile CONSTANT) Q_PROPERTY(int userVerified READ getUserStatus NOTIFY userStatusChanged) Q_PROPERTY(bool isLoading READ isLoading NOTIFY loadingChanged) @@ -133,6 +133,7 @@ signals: void avatarUrlChanged(); void displayError(const QString &errorMessage); void globalUsernameRetrieved(const QString &globalUser); + void devicesChanged(); public slots: void updateAvatarUrl(); -- 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/Cache.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 From d8c0d4874bb1864a677ae451d93727ab75484f84 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 11 Jun 2021 13:12:43 +0200 Subject: Render community items --- resources/qml/Avatar.qml | 2 + resources/qml/ChatPage.qml | 13 ++- resources/qml/CommunitiesList.qml | 151 ++++++++++++++++++++++++++ resources/qml/RoomList.qml | 6 +- resources/qml/device-verification/Success.qml | 1 + resources/res.qrc | 1 + src/Cache.cpp | 4 + src/Olm.cpp | 9 +- src/timeline/CommunitiesModel.cpp | 7 +- 9 files changed, 180 insertions(+), 14 deletions(-) create mode 100644 resources/qml/CommunitiesList.qml (limited to 'src/Cache.cpp') diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index 84c22da1..9eb3380e 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -14,6 +14,7 @@ Rectangle { property alias url: img.source property string userid property string displayName + property alias textColor: label.color signal clicked(var mouse) @@ -26,6 +27,7 @@ Rectangle { } Label { + id: label anchors.fill: parent text: TimelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "") textFormat: Text.RichText diff --git a/resources/qml/ChatPage.qml b/resources/qml/ChatPage.qml index 0f884d75..5ccdd9f1 100644 --- a/resources/qml/ChatPage.qml +++ b/resources/qml/ChatPage.qml @@ -23,13 +23,14 @@ Rectangle { AdaptiveLayoutElement { id: communityListC - minimumWidth: Nheko.avatarSize * 2 + Nheko.paddingSmall * 2 - collapsedWidth: Nheko.avatarSize + Nheko.paddingSmall * 2 - preferredWidth: Nheko.avatarSize + Nheko.paddingSmall * 2 - maximumWidth: Nheko.avatarSize * 7 + Nheko.paddingSmall * 2 + minimumWidth: communitiesList.avatarSize * 4 + Nheko.paddingMedium * 2 + collapsedWidth: communitiesList.avatarSize + 2* Nheko.paddingMedium + preferredWidth: collapsedWidth + maximumWidth: communitiesList.avatarSize * 10 + 2* Nheko.paddingMedium - Rectangle { - color: Nheko.theme.sidebarBackground + CommunitiesList { + id: communitiesList + collapsed: parent.collapsed } } diff --git a/resources/qml/CommunitiesList.qml b/resources/qml/CommunitiesList.qml new file mode 100644 index 00000000..6ca619c4 --- /dev/null +++ b/resources/qml/CommunitiesList.qml @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import "./dialogs" +import Qt.labs.platform 1.1 as Platform +import QtQml 2.13 +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.3 +import im.nheko 1.0 + + +Page { + //leftPadding: Nheko.paddingSmall + //rightPadding: Nheko.paddingSmall + property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6) + property bool collapsed: false + + ListView { + id: communitiesList + + anchors.left: parent.left + anchors.right: parent.right + height: parent.height + model: Communities + + ScrollHelper { + flickable: parent + anchors.fill: parent + enabled: !Settings.mobileMode + } + + Platform.Menu { + id: communityContextMenu + + property string id + + function show(id_, tags_) { + id = id_; + open(); + } + + Platform.MenuItem { + text: qsTr("Leave room") + onTriggered: Rooms.leave(roomContextMenu.roomid) + } + + } + + delegate: Rectangle { + id: communityItem + + 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: avatarSize + 2 * Nheko.paddingMedium + width: ListView.view.width + state: "normal" + ToolTip.visible: hovered.hovered && collapsed + ToolTip.text: model.tooltip + states: [ + State { + name: "highlight" + when: hovered.hovered && !(Communities.currentTagId == model.id) + + PropertyChanges { + target: communityItem + background: Nheko.colors.dark + importantText: Nheko.colors.brightText + unimportantText: Nheko.colors.brightText + bubbleBackground: Nheko.colors.highlight + bubbleText: Nheko.colors.highlightedText + } + + }, + State { + name: "selected" + when: Communities.currentTagId == model.id + + PropertyChanges { + target: communityItem + background: Nheko.colors.highlight + importantText: Nheko.colors.highlightedText + unimportantText: Nheko.colors.highlightedText + bubbleBackground: Nheko.colors.highlightedText + bubbleText: Nheko.colors.highlight + } + + } + ] + + TapHandler { + margin: -Nheko.paddingSmall + acceptedButtons: Qt.RightButton + onSingleTapped: communityContextMenu.show(model.id); + + gesturePolicy: TapHandler.ReleaseWithinBounds + } + + TapHandler { + margin: -Nheko.paddingSmall + onSingleTapped: Communities.setCurrentTagId(model.id) + onLongPressed: communityContextMenu.show(model.id) + } + + HoverHandler { + id: hovered + + margin: -Nheko.paddingSmall + } + + RowLayout { + spacing: Nheko.paddingMedium + anchors.fill: parent + anchors.margins: Nheko.paddingMedium + + Avatar { + id: avatar + + enabled: false + Layout.alignment: Qt.AlignVCenter + height: avatarSize + width: avatarSize + url: { + if (model.avatarUrl.startsWith("mxc://")) { + return model.avatarUrl.replace("mxc://", "image://MxcImage/") + } else { + return "image://colorimage/"+model.avatarUrl+"?" + communityItem.unimportantText + } + } + displayName: model.displayName + color: communityItem.background + + } + + } + + } + + } + + background: Rectangle { + color: Nheko.theme.sidebarBackground + } + +} diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index a6637467..09fb3701 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -32,8 +32,8 @@ Page { Connections { onActiveTimelineChanged: { - roomlist.positionViewAtIndex(Rooms.roomidToIndex(TimelineManager.timeline.roomId()), ListView.Contain); - console.log("Test" + TimelineManager.timeline.roomId() + " " + Rooms.roomidToIndex(TimelineManager.timeline.roomId)); + roomlist.positionViewAtIndex(Rooms.roomidToIndex(Rooms.currentRoom.roomId()), ListView.Contain); + console.log("Test" + Rooms.currentRoom.roomId() + " " + Rooms.roomidToIndex(Rooms.currentRoom.roomId())); } target: TimelineManager } @@ -121,7 +121,7 @@ Page { states: [ State { name: "highlight" - when: hovered.hovered && !(TimelineManager.timeline && model.roomId == TimelineManager.timeline.roomId()) + when: hovered.hovered && !(Rooms.currentRoom && model.roomId == Rooms.currentRoom.roomId()) PropertyChanges { target: roomItem diff --git a/resources/qml/device-verification/Success.qml b/resources/qml/device-verification/Success.qml index b858a1a1..70cfafaf 100644 --- a/resources/qml/device-verification/Success.qml +++ b/resources/qml/device-verification/Success.qml @@ -5,6 +5,7 @@ import QtQuick 2.3 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.10 +import im.nheko 1.0 Pane { property string title: qsTr("Successful Verification") diff --git a/resources/res.qrc b/resources/res.qrc index 531e9be2..53c74ae3 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -125,6 +125,7 @@ qml/Root.qml qml/ChatPage.qml + qml/CommunitiesList.qml qml/RoomList.qml qml/TimelineView.qml qml/Avatar.qml diff --git a/src/Cache.cpp b/src/Cache.cpp index 4a99dd59..5684de37 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -3451,6 +3451,10 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query if (!updateToWrite.master_keys.keys.empty() && update.master_keys.keys != updateToWrite.master_keys.keys) { + nhlog::db()->debug("Master key of {} changed:\nold: {}\nnew: {}", + user, + updateToWrite.master_keys.keys.size(), + update.master_keys.keys.size()); updateToWrite.master_key_changed = true; } diff --git a/src/Olm.cpp b/src/Olm.cpp index d08c1b3e..ff4c883b 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -206,8 +206,11 @@ handle_olm_message(const OlmMessage &msg) for (const auto &cipher : msg.ciphertext) { // We skip messages not meant for the current device. - if (cipher.first != my_key) + if (cipher.first != my_key) { + nhlog::crypto()->debug( + "Skipping message for {} since we are {}.", cipher.first, my_key); continue; + } const auto type = cipher.second.type; nhlog::crypto()->info("type: {}", type == 0 ? "OLM_PRE_KEY" : "OLM_MESSAGE"); @@ -661,8 +664,10 @@ try_olm_decryption(const std::string &sender_key, const mtx::events::msg::OlmCip for (const auto &id : session_ids) { auto session = cache::getOlmSession(sender_key, id); - if (!session) + if (!session) { + nhlog::crypto()->warn("Unknown olm session: {}:{}", sender_key, id); continue; + } mtx::crypto::BinaryBuf text; diff --git a/src/timeline/CommunitiesModel.cpp b/src/timeline/CommunitiesModel.cpp index cedaacce..c8ebaa96 100644 --- a/src/timeline/CommunitiesModel.cpp +++ b/src/timeline/CommunitiesModel.cpp @@ -21,6 +21,7 @@ CommunitiesModel::roleNames() const {DisplayName, "displayName"}, {Tooltip, "tooltip"}, {ChildrenHidden, "childrenHidden"}, + {Id, "id"}, }; } @@ -74,9 +75,9 @@ 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.right(2); + return tag.mid(2); case CommunitiesModel::Roles::Tooltip: - return tag.right(2); + return tag.mid(2); } } @@ -143,7 +144,7 @@ void CommunitiesModel::setCurrentTagId(QString tagId) { if (tagId.startsWith("tag:")) { - auto tag = tagId.remove(0, 4); + auto tag = tagId.mid(4); for (const auto &t : tags_) { if (t == tag) { this->currentTagId_ = tagId; -- cgit 1.5.1 From e6878ee298525ac7808595418c4b84b93788ff2e Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 12 Jun 2021 16:05:45 +0200 Subject: Don't read avatarUrl from local profile, if no global avatar is set --- resources/qml/RoomList.qml | 12 +++++++----- src/Cache.cpp | 3 ++- src/ui/UserProfile.cpp | 8 +++----- 3 files changed, 12 insertions(+), 11 deletions(-) (limited to 'src/Cache.cpp') diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index 09fb3701..f31fce60 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -447,6 +447,8 @@ Page { RowLayout { id: userInfoGrid + property var profile: Nheko.currentUser + spacing: Nheko.paddingMedium anchors.fill: parent anchors.margins: Nheko.paddingMedium @@ -457,9 +459,9 @@ Page { Layout.alignment: Qt.AlignVCenter Layout.preferredWidth: fontMetrics.lineSpacing * 2 Layout.preferredHeight: fontMetrics.lineSpacing * 2 - url: Nheko.currentUser.avatarUrl.replace("mxc://", "image://MxcImage/") - displayName: Nheko.currentUser.displayName - userid: Nheko.currentUser.userid + url: (userInfoGrid.profile ? userInfoGrid.profile.avatarUrl : "").replace("mxc://", "image://MxcImage/") + displayName: userInfoGrid.profile ? userInfoGrid.profile.displayName : "" + userid: userInfoGrid.profile ? userInfoGrid.profile.userid : "" } ColumnLayout { @@ -476,7 +478,7 @@ Page { Layout.alignment: Qt.AlignBottom font.pointSize: fontMetrics.font.pointSize * 1.1 font.weight: Font.DemiBold - fullText: Nheko.currentUser.displayName + fullText: userInfoGrid.profile ? userInfoGrid.profile.displayName : "" elideWidth: col.width } @@ -486,7 +488,7 @@ Page { font.weight: Font.Thin font.pointSize: fontMetrics.font.pointSize * 0.9 elideWidth: col.width - fullText: Nheko.currentUser.userid + fullText: userInfoGrid.profile ? userInfoGrid.profile.userid : "" } } diff --git a/src/Cache.cpp b/src/Cache.cpp index 5684de37..0d75ac51 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -2479,7 +2479,8 @@ Cache::getMember(const std::string &room_id, const std::string &user_id) return m; } } catch (std::exception &e) { - nhlog::db()->warn("Failed to read member ({}): {}", user_id, e.what()); + nhlog::db()->warn( + "Failed to read member ({}) in room ({}): {}", user_id, room_id, e.what()); } return std::nullopt; } diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index da130242..3d9c4b6a 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -39,7 +39,8 @@ UserProfile::UserProfile(QString roomid, getGlobalProfileData(); } - if (!cache::client() || !cache::client()->isDatabaseReady()) + if (!cache::client() || !cache::client()->isDatabaseReady() || + !ChatPage::instance()->timelineManager()) return; connect(cache::client(), @@ -127,10 +128,7 @@ UserProfile::displayName() QString UserProfile::avatarUrl() { - return (isGlobalUserProfile() && globalAvatarUrl != "") - ? globalAvatarUrl - : cache::avatarUrl(roomid_, userid_); - ; + return isGlobalUserProfile() ? globalAvatarUrl : cache::avatarUrl(roomid_, userid_); } bool -- cgit 1.5.1