From 77a0c574bfc962b6c37426fb16a70ca16c08a3f5 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 29 May 2021 21:09:21 -0400 Subject: [PATCH 01/56] QML the room member list --- CMakeLists.txt | 4 +- resources/qml/RoomMembers.qml | 111 ++++++++++++++++++++ resources/qml/TopBar.qml | 2 +- resources/res.qrc | 1 + src/MainWindow.cpp | 10 +- src/MainWindow.h | 1 - src/MemberList.cpp | 91 +++++++++++++++++ src/MemberList.h | 58 +++++++++++ src/dialogs/MemberList.cpp | 146 --------------------------- src/dialogs/MemberList.h | 57 ----------- src/main.cpp | 3 + src/timeline/TimelineModel.cpp | 12 ++- src/timeline/TimelineModel.h | 5 +- src/timeline/TimelineViewManager.cpp | 7 +- src/timeline/TimelineViewManager.h | 1 - 15 files changed, 284 insertions(+), 225 deletions(-) create mode 100644 resources/qml/RoomMembers.qml create mode 100644 src/MemberList.cpp create mode 100644 src/MemberList.h delete mode 100644 src/dialogs/MemberList.cpp delete mode 100644 src/dialogs/MemberList.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b26b2e5..84f52766 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,7 +285,6 @@ set(SRC_FILES src/dialogs/JoinRoom.cpp src/dialogs/LeaveRoom.cpp src/dialogs/Logout.cpp - src/dialogs/MemberList.cpp src/dialogs/PreviewUploadOverlay.cpp src/dialogs/ReCaptcha.cpp src/dialogs/ReadReceipts.cpp @@ -351,6 +350,7 @@ set(SRC_FILES src/LoginPage.cpp src/MainWindow.cpp src/MatrixClient.cpp + src/MemberList.cpp src/MxcImageProvider.cpp src/Olm.cpp src/RegisterPage.cpp @@ -496,7 +496,6 @@ qt5_wrap_cpp(MOC_HEADERS src/dialogs/JoinRoom.h src/dialogs/LeaveRoom.h src/dialogs/Logout.h - src/dialogs/MemberList.h src/dialogs/PreviewUploadOverlay.h src/dialogs/RawMessage.h src/dialogs/ReCaptcha.h @@ -557,6 +556,7 @@ qt5_wrap_cpp(MOC_HEADERS src/InviteeItem.h src/LoginPage.h src/MainWindow.h + src/MemberList.h src/MxcImageProvider.h src/RegisterPage.h src/SSOHandler.h diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml new file mode 100644 index 00000000..4406c1b0 --- /dev/null +++ b/resources/qml/RoomMembers.qml @@ -0,0 +1,111 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Window 2.12 +import im.nheko 1.0 + +ApplicationWindow { + id: roomMembersRoot + + property string roomName: Rooms.currentRoom.roomName + property MemberList members + + title: qsTr("Members of ") + roomName + x: MainWindow.x + (MainWindow.width / 2) - (width / 2) + y: MainWindow.y + (MainWindow.height / 2) - (height / 2) + height: 650 + width: 420 + minimumHeight: 420 + + Shortcut { + sequence: StandardKey.Cancel + onActivated: roomMembersRoot.close() + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 10 + spacing: 10 + + Avatar { + id: roomAvatar + + width: 130 + height: width + displayName: members.roomName + Layout.alignment: Qt.AlignHCenter + url: members.avatarUrl.replace("mxc://", "image://MxcImage/") + onClicked: TimelineManager.timeline.openRoomSettings(members.roomId) + } + + Label { + font.pixelSize: 24 + text: members.memberCount + (members.memberCount === 1 ? qsTr(" person in ") : qsTr(" people in ")) + roomName + Layout.alignment: Qt.AlignHCenter + } + + ScrollView { + clip: false + palette: colors + padding: 10 + ScrollBar.horizontal.visible: false + Layout.fillHeight: true + Layout.minimumHeight: 200 + Layout.fillWidth: true + + ListView { + id: memberList + + clip: true + spacing: 8 + boundsBehavior: Flickable.StopAtBounds + model: members + + ScrollHelper { + flickable: parent + anchors.fill: parent + enabled: !Settings.mobileMode + } + + delegate: RowLayout { + spacing: 10 + + Avatar { + width: avatarSize + height: avatarSize + userid: model.mxid + url: model.avatarUrl.replace("mxc://", "image://MxcImage/") + displayName: model.displayName + onClicked: TimelineManager.timeline.openUserProfile(model.mxid) + } + + ColumnLayout { + spacing: 5 + + Label { + text: model.displayName + color: TimelineManager.userColor(model ? model.mxid : "", colors.window) + font.pointSize: 12 + } + + Label { + text: model.mxid + color: colors.buttonText + font.pointSize: 10 + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + } + } + } + } + + footer: DialogButtonBox { + standardButtons: DialogButtonBox.Ok + onAccepted: roomMembersRoot.close() + } +} diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml index 58aba0c7..50c2447c 100644 --- a/resources/qml/TopBar.qml +++ b/resources/qml/TopBar.qml @@ -116,7 +116,7 @@ Rectangle { Platform.MenuItem { text: qsTr("Members") - onTriggered: TimelineManager.openMemberListDialog(room.roomId()) + onTriggered: Rooms.currentRoom.openRoomMembers(room.roomId()) } Platform.MenuItem { diff --git a/resources/res.qrc b/resources/res.qrc index e9479e57..da5288c8 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -185,6 +185,7 @@ qml/components/AdaptiveLayout.qml qml/components/AdaptiveLayoutElement.qml qml/components/FlatButton.qml + qml/RoomMembers.qml media/ring.ogg diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index ed337ca4..36bada83 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -21,6 +21,7 @@ #include "LoginPage.h" #include "MainWindow.h" #include "MatrixClient.h" +#include "MemberList.h" #include "RegisterPage.h" #include "TrayIcon.h" #include "UserSettingsPage.h" @@ -36,7 +37,6 @@ #include "dialogs/JoinRoom.h" #include "dialogs/LeaveRoom.h" #include "dialogs/Logout.h" -#include "dialogs/MemberList.h" #include "dialogs/ReadReceipts.h" MainWindow *MainWindow::instance_ = nullptr; @@ -310,14 +310,6 @@ MainWindow::hasActiveUser() settings.contains(prefix + "auth/user_id"); } -void -MainWindow::openMemberListDialog(const QString &room_id) -{ - auto dialog = new dialogs::MemberList(room_id, this); - - showDialog(dialog); -} - void MainWindow::openLeaveRoomDialog(const QString &room_id) { diff --git a/src/MainWindow.h b/src/MainWindow.h index 3571f079..6d62545c 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -65,7 +65,6 @@ public: std::function callback); void openJoinRoomDialog(std::function callback); void openLogoutDialog(); - void openMemberListDialog(const QString &room_id); void openReadReceiptsDialog(const QString &event_id); void hideOverlay(); diff --git a/src/MemberList.cpp b/src/MemberList.cpp new file mode 100644 index 00000000..62488277 --- /dev/null +++ b/src/MemberList.cpp @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MemberList.h" + +#include "Cache.h" +#include "ChatPage.h" +#include "Config.h" +#include "Logging.h" +#include "Utils.h" +#include "timeline/TimelineViewManager.h" +#include "ui/Avatar.h" + +MemberList::MemberList(const QString &room_id, QWidget *parent) + : QAbstractListModel{parent} + , room_id_{room_id} +{ + try { + info_ = cache::singleRoomInfo(room_id_.toStdString()); + } catch (const lmdb::error &) { + nhlog::db()->warn("failed to retrieve room info from cache: {}", + room_id_.toStdString()); + } + + try { + addUsers(cache::getMembers(room_id_.toStdString())); + } catch (const lmdb::error &e) { + nhlog::db()->critical("Failed to retrieve members from cache: {}", e.what()); + } +} + +void +MemberList::addUsers(const std::vector &members) +{ + beginInsertRows(QModelIndex{}, m_memberList.count(), m_memberList.count() + members.size() - 1); + + for (const auto &member : members) + m_memberList.push_back( + {member, + ChatPage::instance()->timelineManager()->rooms()->currentRoom()->avatarUrl( + member.user_id)}); + + endInsertRows(); +} + +QHash +MemberList::roleNames() const +{ + return {{Mxid, "mxid"}, {DisplayName, "displayName"}, {AvatarUrl, "avatarUrl"}}; +} + +QVariant +MemberList::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= (int)m_memberList.size() || index.row() < 0) + return {}; + + switch (role) { + case Mxid: + return m_memberList[index.row()].first.user_id; + case DisplayName: + return m_memberList[index.row()].first.display_name; + case AvatarUrl: + return m_memberList[index.row()].second; + default: + return {}; + } +} + +bool MemberList::canFetchMore(const QModelIndex &) const +{ + const size_t numMembers = rowCount(); + return (numMembers > 1 && numMembers < info_.member_count); +} + +void +MemberList::fetchMore(const QModelIndex &) +{ + addUsers(cache::getMembers(room_id_.toStdString(), rowCount())); +} diff --git a/src/MemberList.h b/src/MemberList.h new file mode 100644 index 00000000..dbe69f4b --- /dev/null +++ b/src/MemberList.h @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "CacheStructs.h" +#include + +class MemberList : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged) + Q_PROPERTY(size_t memberCount READ memberCount NOTIFY memberCountChanged) + Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged) + Q_PROPERTY(QString roomId READ roomId NOTIFY roomIdChanged) + +public: + enum Roles + { + Mxid, + DisplayName, + AvatarUrl, + }; + MemberList(const QString &room_id, QWidget *parent = nullptr); + + QHash roleNames() const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override + { + Q_UNUSED(parent) + return static_cast(m_memberList.size()); + } + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + QString roomName() const { return QString::fromStdString(info_.name); } + size_t memberCount() const { return info_.member_count; } + QString avatarUrl() const { return QString::fromStdString(info_.avatar_url); } + QString roomId() const { return room_id_; } + +signals: + void roomNameChanged(); + void memberCountChanged(); + void avatarUrlChanged(); + void roomIdChanged(); + +public slots: + void addUsers(const std::vector &users); + +protected: + bool canFetchMore(const QModelIndex &) const; + void fetchMore(const QModelIndex &); + +private: + QVector> m_memberList; + QString room_id_; + RoomInfo info_; +}; diff --git a/src/dialogs/MemberList.cpp b/src/dialogs/MemberList.cpp deleted file mode 100644 index 21eb72b0..00000000 --- a/src/dialogs/MemberList.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dialogs/MemberList.h" - -#include "Cache.h" -#include "ChatPage.h" -#include "Config.h" -#include "Logging.h" -#include "Utils.h" -#include "ui/Avatar.h" - -using namespace dialogs; - -MemberItem::MemberItem(const RoomMember &member, QWidget *parent) - : QWidget(parent) -{ - topLayout_ = new QHBoxLayout(this); - topLayout_->setMargin(0); - - textLayout_ = new QVBoxLayout; - textLayout_->setMargin(0); - textLayout_->setSpacing(0); - - avatar_ = new Avatar(this, 44); - avatar_->setLetter(utils::firstChar(member.display_name)); - - avatar_->setImage(ChatPage::instance()->currentRoom(), member.user_id); - - QFont nameFont; - nameFont.setPointSizeF(nameFont.pointSizeF() * 1.1); - - userId_ = new QLabel(member.user_id, this); - userName_ = new QLabel(member.display_name, this); - userName_->setFont(nameFont); - - textLayout_->addWidget(userName_); - textLayout_->addWidget(userId_); - - topLayout_->addWidget(avatar_); - topLayout_->addLayout(textLayout_, 1); -} - -void -MemberItem::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -MemberList::MemberList(const QString &room_id, QWidget *parent) - : QFrame(parent) - , room_id_{room_id} -{ - setAutoFillBackground(true); - setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); - setWindowModality(Qt::WindowModal); - setAttribute(Qt::WA_DeleteOnClose, true); - - auto layout = new QVBoxLayout(this); - layout->setSpacing(conf::modals::WIDGET_SPACING); - layout->setMargin(conf::modals::WIDGET_MARGIN); - - list_ = new QListWidget; - list_->setFrameStyle(QFrame::NoFrame); - list_->setSelectionMode(QAbstractItemView::NoSelection); - list_->setSpacing(5); - - QFont largeFont; - largeFont.setPointSizeF(largeFont.pointSizeF() * 1.5); - - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - setMinimumHeight(list_->sizeHint().height() * 2); - setMinimumWidth(std::max(list_->sizeHint().width() + 4 * conf::modals::WIDGET_MARGIN, - QFontMetrics(largeFont).averageCharWidth() * 30 - - 2 * conf::modals::WIDGET_MARGIN)); - - QFont font; - font.setPointSizeF(font.pointSizeF() * conf::modals::LABEL_MEDIUM_SIZE_RATIO); - - topLabel_ = new QLabel(tr("Room members"), this); - topLabel_->setAlignment(Qt::AlignCenter); - topLabel_->setFont(font); - - auto okBtn = new QPushButton(tr("OK"), this); - - auto buttonLayout = new QHBoxLayout(); - buttonLayout->setSpacing(15); - buttonLayout->addStretch(1); - buttonLayout->addWidget(okBtn); - - layout->addWidget(topLabel_); - layout->addWidget(list_); - layout->addLayout(buttonLayout); - - list_->clear(); - - connect(list_->verticalScrollBar(), &QAbstractSlider::valueChanged, this, [this](int pos) { - if (pos != list_->verticalScrollBar()->maximum()) - return; - - const size_t numMembers = list_->count() - 1; - - if (numMembers > 0) - addUsers(cache::getMembers(room_id_.toStdString(), numMembers)); - }); - - try { - addUsers(cache::getMembers(room_id_.toStdString())); - } catch (const lmdb::error &e) { - nhlog::db()->critical("Failed to retrieve members from cache: {}", e.what()); - } - - auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this); - connect(closeShortcut, &QShortcut::activated, this, &MemberList::close); - connect(okBtn, &QPushButton::clicked, this, &MemberList::close); -} - -void -MemberList::addUsers(const std::vector &members) -{ - for (const auto &member : members) { - auto user = new MemberItem(member, this); - auto item = new QListWidgetItem; - - item->setSizeHint(user->minimumSizeHint()); - item->setFlags(Qt::NoItemFlags); - item->setTextAlignment(Qt::AlignCenter); - - list_->insertItem(list_->count() - 1, item); - list_->setItemWidget(item, user); - } -} diff --git a/src/dialogs/MemberList.h b/src/dialogs/MemberList.h deleted file mode 100644 index b822eec8..00000000 --- a/src/dialogs/MemberList.h +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include - -class Avatar; -class QPushButton; -class QHBoxLayout; -class QLabel; -class QVBoxLayout; - -struct RoomMember; - -template -class QSharedPointer; - -namespace dialogs { - -class MemberItem : public QWidget -{ - Q_OBJECT - -public: - MemberItem(const RoomMember &member, QWidget *parent); - -protected: - void paintEvent(QPaintEvent *) override; - -private: - QHBoxLayout *topLayout_; - QVBoxLayout *textLayout_; - - Avatar *avatar_; - - QLabel *userName_; - QLabel *userId_; -}; - -class MemberList : public QFrame -{ - Q_OBJECT -public: - MemberList(const QString &room_id, QWidget *parent = nullptr); - -public slots: - void addUsers(const std::vector &users); - -private: - QString room_id_; - QLabel *topLabel_; - QListWidget *list_; -}; -} // dialogs diff --git a/src/main.cpp b/src/main.cpp index 29e93d49..376fc4f5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -205,6 +205,9 @@ main(int argc, char *argv[]) parser.process(app); + // make sure that size_t properties will work + qRegisterMetaType("size_t"); + app.setWindowIcon(QIcon::fromTheme("nheko", QIcon{":/logos/nheko.png"})); http::init(); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 7b3f0729..48d69493 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -25,6 +25,7 @@ #include "Logging.h" #include "MainWindow.h" #include "MatrixClient.h" +#include "MemberList.h" #include "MxcImageProvider.h" #include "Olm.h" #include "TimelineViewManager.h" @@ -1061,9 +1062,16 @@ TimelineModel::openUserProfile(QString userid) } void -TimelineModel::openRoomSettings() +TimelineModel::openRoomMembers() { - RoomSettings *settings = new RoomSettings(roomId(), this); + MemberList *memberList = new MemberList(roomId()); + emit openRoomMembersDialog(memberList); +} + +void +TimelineModel::openRoomSettings(QString room_id) +{ + RoomSettings *settings = new RoomSettings(room_id == QString() ? roomId() : room_id, this); connect(this, &TimelineModel::roomAvatarUrlChanged, settings, &RoomSettings::avatarChanged); openRoomSettingsDialog(settings); } diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 3c80ade8..feb7b5f5 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -17,6 +17,7 @@ #include "CacheStructs.h" #include "EventStore.h" #include "InputBar.h" +#include "MemberList.h" #include "Permissions.h" #include "ui/RoomSettings.h" #include "ui/UserProfile.h" @@ -235,7 +236,8 @@ public: Q_INVOKABLE void forwardMessage(QString eventId, QString roomId); Q_INVOKABLE void viewDecryptedRawMessage(QString id) const; Q_INVOKABLE void openUserProfile(QString userid); - Q_INVOKABLE void openRoomSettings(); + Q_INVOKABLE void openRoomMembers(); + Q_INVOKABLE void openRoomSettings(QString room_id = QString()); Q_INVOKABLE void editAction(QString id); Q_INVOKABLE void replyAction(QString id); Q_INVOKABLE void readReceiptsAction(QString id) const; @@ -352,6 +354,7 @@ signals: void lastMessageChanged(); void notificationsChanged(); + void openRoomMembersDialog(MemberList *members); void openRoomSettingsDialog(RoomSettings *settings); void newMessageToSend(mtx::events::collections::TimelineEvents event); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 3e69f92b..011ff61c 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -174,6 +174,8 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par 0, "UserProfileModel", "UserProfile needs to be instantiated on the C++ side"); + qmlRegisterUncreatableType( + "im.nheko", 1, 0, "MemberList", "MemberList needs to be instantiated on the C++ side"); qmlRegisterUncreatableType( "im.nheko", 1, @@ -428,11 +430,6 @@ TimelineViewManager::openInviteUsersDialog() [this](const QStringList &invitees) { emit inviteUsers(invitees); }); } void -TimelineViewManager::openMemberListDialog(QString roomid) const -{ - MainWindow::instance()->openMemberListDialog(roomid); -} -void TimelineViewManager::openLeaveRoomDialog(QString roomid) const { MainWindow::instance()->openLeaveRoomDialog(roomid); diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 15b4f523..39d0d31c 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -66,7 +66,6 @@ public: Q_INVOKABLE void focusMessageInput(); Q_INVOKABLE void openInviteUsersDialog(); - Q_INVOKABLE void openMemberListDialog(QString roomid) const; Q_INVOKABLE void openLeaveRoomDialog(QString roomid) const; Q_INVOKABLE void removeVerificationFlow(DeviceVerificationFlow *flow); From 6c57fa6c5b491e981958e417458edac40e9000b4 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Thu, 10 Jun 2021 20:11:49 -0400 Subject: [PATCH 02/56] QML the invite dialog This also adds a property `roomId` to TimelineModel. --- CMakeLists.txt | 4 - resources/qml/InviteDialog.qml | 112 +++++++++++++++++++ resources/qml/TopBar.qml | 14 ++- resources/qml/types/Invitee.qml | 5 + resources/res.qrc | 13 +-- src/ChatPage.cpp | 28 +++++ src/InviteeItem.cpp | 28 ----- src/InviteeItem.h | 31 ------ src/MainWindow.cpp | 13 --- src/dialogs/InviteUsers.cpp | 158 --------------------------- src/dialogs/InviteUsers.h | 45 -------- src/timeline/TimelineModel.h | 1 + src/timeline/TimelineViewManager.cpp | 51 +++++++++ src/timeline/TimelineViewManager.h | 5 +- 14 files changed, 215 insertions(+), 293 deletions(-) create mode 100644 resources/qml/InviteDialog.qml create mode 100644 resources/qml/types/Invitee.qml delete mode 100644 src/InviteeItem.cpp delete mode 100644 src/InviteeItem.h delete mode 100644 src/dialogs/InviteUsers.cpp delete mode 100644 src/dialogs/InviteUsers.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 84f52766..56592950 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -281,7 +281,6 @@ set(SRC_FILES src/dialogs/CreateRoom.cpp src/dialogs/FallbackAuth.cpp src/dialogs/ImageOverlay.cpp - src/dialogs/InviteUsers.cpp src/dialogs/JoinRoom.cpp src/dialogs/LeaveRoom.cpp src/dialogs/Logout.cpp @@ -345,7 +344,6 @@ set(SRC_FILES src/CompletionProxyModel.cpp src/DeviceVerificationFlow.cpp src/EventAccessors.cpp - src/InviteeItem.cpp src/Logging.cpp src/LoginPage.cpp src/MainWindow.cpp @@ -492,7 +490,6 @@ qt5_wrap_cpp(MOC_HEADERS src/dialogs/CreateRoom.h src/dialogs/FallbackAuth.h src/dialogs/ImageOverlay.h - src/dialogs/InviteUsers.h src/dialogs/JoinRoom.h src/dialogs/LeaveRoom.h src/dialogs/Logout.h @@ -553,7 +550,6 @@ qt5_wrap_cpp(MOC_HEADERS src/Clipboard.h src/CompletionProxyModel.h src/DeviceVerificationFlow.h - src/InviteeItem.h src/LoginPage.h src/MainWindow.h src/MemberList.h diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml new file mode 100644 index 00000000..5d3a8f1e --- /dev/null +++ b/resources/qml/InviteDialog.qml @@ -0,0 +1,112 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import im.nheko 1.0 +import "./types" + +ApplicationWindow { + id: inviteDialogRoot + + property string roomId + property string roomName + property list invitees + + function addInvite() { + if (inviteeEntry.text.match("@.+?:.{3,}")) + { + invitees.push(inviteeComponent.createObject( + inviteDialogRoot, { + "invitee": inviteeEntry.text + })); + inviteeEntry.clear(); + } + } + + function accept() { + if (inviteeEntry.text !== "") + addInvite(); + + var inviteeStringList = ["temp"]; // the "temp" element exists to declare this as a string array + for (var i = 0; i < invitees.length; ++i) + inviteeStringList.push(invitees[i].invitee); + inviteeStringList.shift(); // remove the first item + + TimelineManager.inviteUsers(inviteDialogRoot.roomId, inviteeStringList); + } + + title: qsTr("Invite users to ") + roomName + x: MainWindow.x + (MainWindow.width / 2) - (width / 2) + y: MainWindow.y + (MainWindow.height / 2) - (height / 2) + height: 380 + width: 340 + + Component { + id: inviteeComponent + + Invitee {} + } + + // TODO: make this work in the TextField + Shortcut { + sequence: "Ctrl+Enter" + onActivated: inviteDialogRoot.accept() + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 10 + spacing: 10 + + Label { + text: qsTr("User ID to invite") + Layout.fillWidth: true + } + + RowLayout { + spacing: 10 + + TextField { + id: inviteeEntry + + placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.") + Layout.fillWidth: true + onAccepted: if (text !== "") addInvite() + } + + Button { + text: qsTr("Invite") + onClicked: if (inviteeEntry.text !== "") addInvite() + } + } + + ListView { + id: inviteesList + + Layout.fillWidth: true + Layout.fillHeight: true + model: invitees + delegate: Label { + text: model.invitee + } + } + } + + footer: DialogButtonBox { + id: buttons + + Button { + text: qsTr("Invite") + DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + onClicked: { + inviteDialogRoot.accept(); + inviteDialogRoot.close(); + } + } + + Button { + text: qsTr("Cancel") + DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole + onClicked: inviteDialogRoot.close(); + } + } +} diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml index 50c2447c..72dbe604 100644 --- a/resources/qml/TopBar.qml +++ b/resources/qml/TopBar.qml @@ -21,6 +21,12 @@ Rectangle { z: 3 color: Nheko.colors.window + Component { + id: inviteDialog + + InviteDialog {} + } + TapHandler { onSingleTapped: { if (room) @@ -111,7 +117,13 @@ Rectangle { Platform.MenuItem { visible: room ? room.permissions.canInvite() : false text: qsTr("Invite users") - onTriggered: TimelineManager.openInviteUsersDialog() + onTriggered: { + var dialog = inviteDialog.createObject(topBar, { + "roomId": room.roomId, + "roomName": room.roomName + }); + dialog.show(); + } } Platform.MenuItem { diff --git a/resources/qml/types/Invitee.qml b/resources/qml/types/Invitee.qml new file mode 100644 index 00000000..fbc0b781 --- /dev/null +++ b/resources/qml/types/Invitee.qml @@ -0,0 +1,5 @@ +import QtQuick 2.12 + +Item { + property string invitee +} diff --git a/resources/res.qrc b/resources/res.qrc index da5288c8..ad7b6665 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -9,7 +9,6 @@ icons/ui/do-not-disturb-rounded-sign@2x.png icons/ui/round-remove-button.png icons/ui/round-remove-button@2x.png - icons/ui/double-tick-indicator.png icons/ui/double-tick-indicator@2x.png icons/ui/lock.png @@ -55,22 +54,17 @@ icons/ui/pause-symbol@2x.png icons/ui/remove-symbol.png icons/ui/remove-symbol@2x.png - icons/ui/world.png icons/ui/world@2x.png - icons/ui/tag.png icons/ui/tag@2x.png icons/ui/star.png icons/ui/star@2x.png icons/ui/lowprio.png icons/ui/lowprio@2x.png - icons/ui/edit.png icons/ui/edit@2x.png - icons/ui/mail-reply.png - icons/ui/place-call.png icons/ui/end-call.png icons/ui/microphone-mute.png @@ -78,7 +72,6 @@ icons/ui/screen-share.png icons/ui/toggle-camera-view.png icons/ui/video-call.png - icons/emoji-categories/people.png icons/emoji-categories/people@2x.png icons/emoji-categories/nature.png @@ -99,16 +92,12 @@ nheko.png nheko.svg - splash.png splash@2x.png - register.png register@2x.png - login.png login@2x.png - nheko-512.png nheko-256.png nheko-128.png @@ -186,6 +175,8 @@ qml/components/AdaptiveLayoutElement.qml qml/components/FlatButton.qml qml/RoomMembers.qml + qml/InviteDialog.qml + qml/types/Invitee.qml media/ring.ogg diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 10a91557..f6ea4539 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -140,6 +140,34 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) } }); + connect( + view_manager_, &TimelineViewManager::showRoomList, splitter, &Splitter::showFullRoomList); + connect( + view_manager_, + &TimelineViewManager::inviteUsers, + this, + [this](QString roomId, QStringList users) { + for (int ii = 0; ii < users.size(); ++ii) { + QTimer::singleShot(ii * 500, this, [this, roomId, ii, users]() { + const auto user = users.at(ii); + + http::client()->invite_user( + roomId.toStdString(), + user.toStdString(), + [this, user](const mtx::responses::RoomInvite &, + mtx::http::RequestErr err) { + if (err) { + emit showNotification( + tr("Failed to invite user: %1").arg(user)); + return; + } + + emit showNotification(tr("Invited user: %1").arg(user)); + }); + }); + } + }); + connect(this, &ChatPage::leftRoom, this, &ChatPage::removeRoom); connect(this, &ChatPage::newRoom, this, &ChatPage::changeRoom, Qt::QueuedConnection); connect(this, &ChatPage::notificationsRetrieved, this, &ChatPage::sendNotifications); diff --git a/src/InviteeItem.cpp b/src/InviteeItem.cpp deleted file mode 100644 index 27f02560..00000000 --- a/src/InviteeItem.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include -#include - -#include "InviteeItem.h" - -constexpr int SidePadding = 10; - -InviteeItem::InviteeItem(mtx::identifiers::User user, QWidget *parent) - : QWidget{parent} - , user_{QString::fromStdString(user.to_string())} -{ - auto topLayout_ = new QHBoxLayout(this); - topLayout_->setSpacing(0); - topLayout_->setContentsMargins(SidePadding, 0, 3 * SidePadding, 0); - - name_ = new QLabel(user_, this); - removeUserBtn_ = new QPushButton(tr("Remove"), this); - - topLayout_->addWidget(name_); - topLayout_->addWidget(removeUserBtn_, 0, Qt::AlignRight); - - connect(removeUserBtn_, &QPushButton::clicked, this, &InviteeItem::removeItem); -} diff --git a/src/InviteeItem.h b/src/InviteeItem.h deleted file mode 100644 index 014541ea..00000000 --- a/src/InviteeItem.h +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - -#include - -class QPushButton; -class QLabel; - -class InviteeItem : public QWidget -{ - Q_OBJECT - -public: - InviteeItem(mtx::identifiers::User user, QWidget *parent = nullptr); - - QString userID() { return user_; } - -signals: - void removeItem(); - -private: - QString user_; - - QLabel *name_; - QPushButton *removeUserBtn_; -}; diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 36bada83..c0486d01 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -33,7 +33,6 @@ #include "ui/SnackBar.h" #include "dialogs/CreateRoom.h" -#include "dialogs/InviteUsers.h" #include "dialogs/JoinRoom.h" #include "dialogs/LeaveRoom.h" #include "dialogs/Logout.h" @@ -333,18 +332,6 @@ MainWindow::showOverlayProgressBar() showSolidOverlayModal(spinner_); } -void -MainWindow::openInviteUsersDialog(std::function callback) -{ - auto dialog = new dialogs::InviteUsers(this); - connect(dialog, &dialogs::InviteUsers::sendInvites, this, [callback](QStringList invitees) { - if (!invitees.isEmpty()) - callback(invitees); - }); - - showDialog(dialog); -} - void MainWindow::openJoinRoomDialog(std::function callback) { diff --git a/src/dialogs/InviteUsers.cpp b/src/dialogs/InviteUsers.cpp deleted file mode 100644 index 9dd6085f..00000000 --- a/src/dialogs/InviteUsers.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dialogs/InviteUsers.h" - -#include "Config.h" -#include "InviteeItem.h" -#include "ui/TextField.h" - -#include - -using namespace dialogs; - -InviteUsers::InviteUsers(QWidget *parent) - : QFrame(parent) -{ - setAutoFillBackground(true); - setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); - setWindowModality(Qt::WindowModal); - setAttribute(Qt::WA_DeleteOnClose, true); - - setMinimumWidth(conf::window::minModalWidth); - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - - auto layout = new QVBoxLayout(this); - layout->setSpacing(conf::modals::WIDGET_SPACING); - layout->setMargin(conf::modals::WIDGET_MARGIN); - - auto buttonLayout = new QHBoxLayout(); - buttonLayout->setSpacing(0); - buttonLayout->setMargin(0); - - confirmBtn_ = new QPushButton("Invite", this); - confirmBtn_->setDefault(true); - cancelBtn_ = new QPushButton(tr("Cancel"), this); - - buttonLayout->addStretch(1); - buttonLayout->setSpacing(15); - buttonLayout->addWidget(cancelBtn_); - buttonLayout->addWidget(confirmBtn_); - - inviteeInput_ = new TextField(this); - inviteeInput_->setLabel(tr("User ID to invite")); - - inviteeList_ = new QListWidget; - inviteeList_->setFrameStyle(QFrame::NoFrame); - inviteeList_->setSelectionMode(QAbstractItemView::NoSelection); - inviteeList_->setAttribute(Qt::WA_MacShowFocusRect, 0); - inviteeList_->setSpacing(5); - - errorLabel_ = new QLabel(this); - errorLabel_->setAlignment(Qt::AlignCenter); - - layout->addWidget(inviteeInput_); - layout->addWidget(errorLabel_); - layout->addWidget(inviteeList_); - layout->addLayout(buttonLayout); - - connect(inviteeInput_, &TextField::returnPressed, this, &InviteUsers::addUser); - connect(confirmBtn_, &QPushButton::clicked, [this]() { - if (!inviteeInput_->text().trimmed().isEmpty()) { - addUser(); - } - - emit sendInvites(invitedUsers()); - - inviteeInput_->clear(); - inviteeList_->clear(); - errorLabel_->hide(); - - emit close(); - }); - - connect(cancelBtn_, &QPushButton::clicked, [this]() { - inviteeInput_->clear(); - inviteeList_->clear(); - errorLabel_->hide(); - - emit close(); - }); -} - -void -InviteUsers::addUser() -{ - auto user_id = inviteeInput_->text(); - - try { - namespace ids = mtx::identifiers; - auto user = ids::parse(user_id.toStdString()); - - auto item = new QListWidgetItem(inviteeList_); - auto invitee = new InviteeItem(user, this); - - item->setSizeHint(invitee->minimumSizeHint()); - item->setFlags(Qt::NoItemFlags); - item->setTextAlignment(Qt::AlignCenter); - - inviteeList_->setItemWidget(item, invitee); - - connect(invitee, &InviteeItem::removeItem, this, [this, item]() { - emit removeInvitee(item); - }); - - errorLabel_->hide(); - inviteeInput_->clear(); - } catch (std::exception &e) { - errorLabel_->setText(e.what()); - errorLabel_->show(); - } -} - -void -InviteUsers::removeInvitee(QListWidgetItem *item) -{ - int row = inviteeList_->row(item); - auto widget = inviteeList_->takeItem(row); - - inviteeList_->removeItemWidget(widget); -} - -QStringList -InviteUsers::invitedUsers() const -{ - QStringList users; - - for (int ii = 0; ii < inviteeList_->count(); ++ii) { - auto item = inviteeList_->item(ii); - auto widget = inviteeList_->itemWidget(item); - auto invitee = qobject_cast(widget); - - if (invitee) - users << invitee->userID(); - else - qDebug() << "Cast InviteeItem failed"; - } - - return users; -} - -void -InviteUsers::showEvent(QShowEvent *event) -{ - inviteeInput_->setFocus(); - - QFrame::showEvent(event); -} diff --git a/src/dialogs/InviteUsers.h b/src/dialogs/InviteUsers.h deleted file mode 100644 index e40183c1..00000000 --- a/src/dialogs/InviteUsers.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include - -class QPushButton; -class QLabel; -class TextField; -class QListWidget; -class QListWidgetItem; - -namespace dialogs { - -class InviteUsers : public QFrame -{ - Q_OBJECT -public: - explicit InviteUsers(QWidget *parent = nullptr); - -protected: - void showEvent(QShowEvent *event) override; - -signals: - void sendInvites(QStringList invitees); - -private slots: - void removeInvitee(QListWidgetItem *item); - -private: - void addUser(); - QStringList invitedUsers() const; - - QPushButton *confirmBtn_; - QPushButton *cancelBtn_; - - TextField *inviteeInput_; - QLabel *errorLabel_; - - QListWidget *inviteeList_; -}; -} // dialogs diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index feb7b5f5..5730fbab 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -159,6 +159,7 @@ class TimelineModel : public QAbstractListModel Q_PROPERTY(QString edit READ edit WRITE setEdit NOTIFY editChanged RESET resetEdit) Q_PROPERTY( bool paginationInProgress READ paginationInProgress NOTIFY paginationInProgressChanged) + Q_PROPERTY(QString roomId READ roomId CONSTANT) Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged) Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY roomAvatarUrlChanged) Q_PROPERTY(QString roomTopic READ roomTopic NOTIFY roomTopicChanged) diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 011ff61c..43b9a646 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -429,6 +429,57 @@ TimelineViewManager::openInviteUsersDialog() MainWindow::instance()->openInviteUsersDialog( [this](const QStringList &invitees) { emit inviteUsers(invitees); }); } + +void +TimelineViewManager::openLink(QString link) const +{ + QUrl url(link); + if (url.scheme() == "https" && url.host() == "matrix.to") { + // handle matrix.to links internally + QString p = url.fragment(QUrl::FullyEncoded); + if (p.startsWith("/")) + p.remove(0, 1); + + auto temp = p.split("?"); + QString query; + if (temp.size() >= 2) + query = QUrl::fromPercentEncoding(temp.takeAt(1).toUtf8()); + + temp = temp.first().split("/"); + auto identifier = QUrl::fromPercentEncoding(temp.takeFirst().toUtf8()); + QString eventId = QUrl::fromPercentEncoding(temp.join('/').toUtf8()); + if (!identifier.isEmpty()) { + if (identifier.startsWith("@")) { + QByteArray uri = + "matrix:u/" + QUrl::toPercentEncoding(identifier.remove(0, 1)); + if (!query.isEmpty()) + uri.append("?" + query.toUtf8()); + ChatPage::instance()->handleMatrixUri(QUrl::fromEncoded(uri)); + } else if (identifier.startsWith("#")) { + QByteArray uri = + "matrix:r/" + QUrl::toPercentEncoding(identifier.remove(0, 1)); + if (!eventId.isEmpty()) + uri.append("/e/" + + QUrl::toPercentEncoding(eventId.remove(0, 1))); + if (!query.isEmpty()) + uri.append("?" + query.toUtf8()); + ChatPage::instance()->handleMatrixUri(QUrl::fromEncoded(uri)); + } else if (identifier.startsWith("!")) { + QByteArray uri = "matrix:roomid/" + + QUrl::toPercentEncoding(identifier.remove(0, 1)); + if (!eventId.isEmpty()) + uri.append("/e/" + + QUrl::toPercentEncoding(eventId.remove(0, 1))); + if (!query.isEmpty()) + uri.append("?" + query.toUtf8()); + ChatPage::instance()->handleMatrixUri(QUrl::fromEncoded(uri)); + } + } + } else { + QDesktopServices::openUrl(url); + } +} + void TimelineViewManager::openLeaveRoomDialog(QString roomid) const { diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 39d0d31c..945ba2d5 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -65,7 +65,6 @@ public: Q_INVOKABLE QString userStatus(QString id) const; Q_INVOKABLE void focusMessageInput(); - Q_INVOKABLE void openInviteUsersDialog(); Q_INVOKABLE void openLeaveRoomDialog(QString roomid) const; Q_INVOKABLE void removeVerificationFlow(DeviceVerificationFlow *flow); @@ -80,7 +79,9 @@ signals: void replyingEventChanged(QString replyingEvent); void replyClosed(); void newDeviceVerificationRequest(DeviceVerificationFlow *flow); - void inviteUsers(QStringList users); + void inviteUsers(QString roomId, QStringList users); + void showRoomList(); + void narrowViewChanged(); void focusChanged(); void focusInput(); void openImageOverlayInternalCb(QString eventId, QImage img); From e1acf5d324615e8c61c469883a6a42933c8f76bc Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Thu, 10 Jun 2021 20:13:12 -0400 Subject: [PATCH 03/56] make lint --- CMakeLists.txt | 2 + resources/qml/InviteDialog.qml | 78 ++++++++++++++++++---------- resources/qml/RoomMembers.qml | 9 ++++ resources/qml/Root.qml | 38 ++++++++++++++ resources/qml/TimelineView.qml | 1 - resources/qml/TopBar.qml | 14 +---- resources/qml/types/Invitee.qml | 5 -- resources/res.qrc | 1 - src/ChatPage.cpp | 43 ++++++++------- src/InviteesModel.cpp | 77 +++++++++++++++++++++++++++ src/InviteesModel.h | 56 ++++++++++++++++++++ src/MemberList.cpp | 10 ++-- src/timeline/TimelineModel.cpp | 10 ++++ src/timeline/TimelineModel.h | 3 ++ src/timeline/TimelineViewManager.cpp | 70 +++++-------------------- 15 files changed, 287 insertions(+), 130 deletions(-) delete mode 100644 resources/qml/types/Invitee.qml create mode 100644 src/InviteesModel.cpp create mode 100644 src/InviteesModel.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 56592950..f77d9978 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -344,6 +344,7 @@ set(SRC_FILES src/CompletionProxyModel.cpp src/DeviceVerificationFlow.cpp src/EventAccessors.cpp + src/InviteesModel.cpp src/Logging.cpp src/LoginPage.cpp src/MainWindow.cpp @@ -550,6 +551,7 @@ qt5_wrap_cpp(MOC_HEADERS src/Clipboard.h src/CompletionProxyModel.h src/DeviceVerificationFlow.h + src/InviteesModel.h src/LoginPage.h src/MainWindow.h src/MemberList.h diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 5d3a8f1e..d5cc4c6d 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -2,50 +2,28 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import im.nheko 1.0 -import "./types" ApplicationWindow { id: inviteDialogRoot property string roomId property string roomName - property list invitees + property InviteesModel invitees function addInvite() { if (inviteeEntry.text.match("@.+?:.{3,}")) { - invitees.push(inviteeComponent.createObject( - inviteDialogRoot, { - "invitee": inviteeEntry.text - })); + invitees.addUser(inviteeEntry.text); inviteeEntry.clear(); } } - function accept() { - if (inviteeEntry.text !== "") - addInvite(); - - var inviteeStringList = ["temp"]; // the "temp" element exists to declare this as a string array - for (var i = 0; i < invitees.length; ++i) - inviteeStringList.push(invitees[i].invitee); - inviteeStringList.shift(); // remove the first item - - TimelineManager.inviteUsers(inviteDialogRoot.roomId, inviteeStringList); - } - title: qsTr("Invite users to ") + roomName x: MainWindow.x + (MainWindow.width / 2) - (width / 2) y: MainWindow.y + (MainWindow.height / 2) - (height / 2) height: 380 width: 340 - Component { - id: inviteeComponent - - Invitee {} - } - // TODO: make this work in the TextField Shortcut { sequence: "Ctrl+Enter" @@ -74,7 +52,7 @@ ApplicationWindow { } Button { - text: qsTr("Invite") + text: qsTr("Add") onClicked: if (inviteeEntry.text !== "") addInvite() } } @@ -85,9 +63,53 @@ ApplicationWindow { Layout.fillWidth: true Layout.fillHeight: true model: invitees - delegate: Label { - text: model.invitee + + delegate: RowLayout { + spacing: 10 + + Avatar { + width: avatarSize + height: avatarSize + userid: model.mxid + url: model.avatarUrl.replace("mxc://", "image://MxcImage/") + displayName: model.displayName + onClicked: TimelineManager.timeline.openUserProfile(model.mxid) + } + + ColumnLayout { + spacing: 5 + + Label { + text: model.displayName + color: TimelineManager.userColor(model ? model.mxid : "", colors.window) + font.pointSize: 12 + } + + Label { + text: model.mxid + color: colors.buttonText + font.pointSize: 10 + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } } +// delegate: RowLayout { +// spacing: 10 + +// Avatar { +// url: model.avatarUrl +// width: 20 +// height: width +// } + +// Label { +// text: model.displayName + " (" + model.mxid + ")" +// } +// } } } @@ -98,7 +120,7 @@ ApplicationWindow { text: qsTr("Invite") DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole onClicked: { - inviteDialogRoot.accept(); + invitees.accept(); inviteDialogRoot.close(); } } diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 4406c1b0..d31fe319 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -44,6 +44,15 @@ ApplicationWindow { Layout.alignment: Qt.AlignHCenter } + ImageButton { + Layout.alignment: Qt.AlignHCenter + image: ":/icons/icons/ui/add-square-button.png" + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Invite more people") + onClicked: TimelineManager.timeline.openInviteUsersDialog() + } + ScrollView { clip: false palette: colors diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml index 5316e20d..ecd0bdb7 100644 --- a/resources/qml/Root.qml +++ b/resources/qml/Root.qml @@ -89,6 +89,12 @@ Page { } } + Component { + id: inviteDialog + + InviteDialog { + } + } Connections { target: TimelineManager @@ -116,6 +122,38 @@ Page { } } + Connections { + target: TimelineManager.timeline + onOpenRoomMembersDialog: { + var membersDialog = roomMembersComponent.createObject(timelineRoot, { + "members": members, + "roomName": TimelineManager.timeline.roomName + }); + membersDialog.show(); + } + } + + Connections { + target: TimelineManager.timeline + onOpenRoomSettingsDialog: { + var roomSettings = roomSettingsComponent.createObject(timelineRoot, { + "roomSettings": settings + }); + roomSettings.show(); + } + } + + Connections { + target: TimelineManager.timeline + onOpenInviteUsersDialog: { + var dialog = inviteDialog.createObject(timelineRoot, { + "roomId": TimelineManager.timeline.roomId, + "roomName": TimelineManager.timeline.roomName + }); + dialog.show(); + } + } + ChatPage { anchors.fill: parent } diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 148a5817..d515b9b4 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -157,7 +157,6 @@ Item { Layout.alignment: Qt.AlignHCenter enabled: false } - MatrixText { text: parent.roomName font.pixelSize: 24 diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml index 72dbe604..6cf747c5 100644 --- a/resources/qml/TopBar.qml +++ b/resources/qml/TopBar.qml @@ -21,12 +21,6 @@ Rectangle { z: 3 color: Nheko.colors.window - Component { - id: inviteDialog - - InviteDialog {} - } - TapHandler { onSingleTapped: { if (room) @@ -117,13 +111,7 @@ Rectangle { Platform.MenuItem { visible: room ? room.permissions.canInvite() : false text: qsTr("Invite users") - onTriggered: { - var dialog = inviteDialog.createObject(topBar, { - "roomId": room.roomId, - "roomName": room.roomName - }); - dialog.show(); - } + onTriggered: TimelineManager.timeline.openInviteUsers() } Platform.MenuItem { diff --git a/resources/qml/types/Invitee.qml b/resources/qml/types/Invitee.qml deleted file mode 100644 index fbc0b781..00000000 --- a/resources/qml/types/Invitee.qml +++ /dev/null @@ -1,5 +0,0 @@ -import QtQuick 2.12 - -Item { - property string invitee -} diff --git a/resources/res.qrc b/resources/res.qrc index ad7b6665..f8c040e4 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -176,7 +176,6 @@ qml/components/FlatButton.qml qml/RoomMembers.qml qml/InviteDialog.qml - qml/types/Invitee.qml media/ring.ogg diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index f6ea4539..8b4cfeef 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -116,32 +116,31 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect(this, &ChatPage::loggedOut, this, &ChatPage::logout); - connect(view_manager_, &TimelineViewManager::inviteUsers, this, [this](QStringList users) { - const auto room_id = currentRoom().toStdString(); + // TODO: once this signal is moved, reenable this +// connect(view_manager_, &TimelineViewManager::inviteUsers, this, [this](QStringList users) { +// const auto room_id = currentRoom().toStdString(); - for (int ii = 0; ii < users.size(); ++ii) { - QTimer::singleShot(ii * 500, this, [this, room_id, ii, users]() { - const auto user = users.at(ii); +// for (int ii = 0; ii < users.size(); ++ii) { +// QTimer::singleShot(ii * 500, this, [this, room_id, ii, users]() { +// const auto user = users.at(ii); - http::client()->invite_user( - room_id, - user.toStdString(), - [this, user](const mtx::responses::RoomInvite &, - mtx::http::RequestErr err) { - if (err) { - emit showNotification( - tr("Failed to invite user: %1").arg(user)); - return; - } +// http::client()->invite_user( +// room_id, +// user.toStdString(), +// [this, user](const mtx::responses::RoomInvite &, +// mtx::http::RequestErr err) { +// if (err) { +// emit showNotification( +// tr("Failed to invite user: %1").arg(user)); +// return; +// } - emit showNotification(tr("Invited user: %1").arg(user)); - }); - }); - } - }); +// emit showNotification(tr("Invited user: %1").arg(user)); +// }); +// }); +// } +// }); - connect( - view_manager_, &TimelineViewManager::showRoomList, splitter, &Splitter::showFullRoomList); connect( view_manager_, &TimelineViewManager::inviteUsers, diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp new file mode 100644 index 00000000..849c5281 --- /dev/null +++ b/src/InviteesModel.cpp @@ -0,0 +1,77 @@ +#include "InviteesModel.h" + +#include "Cache.h" +#include "Logging.h" +#include "MatrixClient.h" +#include "mtx/responses/profile.hpp" + +InviteesModel::InviteesModel(QObject *parent) + : QAbstractListModel{parent} +{} + +void +InviteesModel::addUser(QString mxid) +{ + beginInsertRows(QModelIndex(), invitees_.count(), invitees_.count()); + + auto invitee = new Invitee{mxid, this}; + connect(invitee, &Invitee::userInfoLoaded, this, [this]() { + emit dataChanged(QModelIndex{}, QModelIndex{}); + }); + + invitees_.push_back(invitee); + + endInsertRows(); +} + +QHash +InviteesModel::roleNames() const +{ + return {{Mxid, "mxid"}, {DisplayName, "displayName"}, {AvatarUrl, "avatarUrl"}}; +} + +QVariant +InviteesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= (int)invitees_.size() || index.row() < 0) + return {}; + + switch (role) { + case Mxid: + return invitees_[index.row()]->mxid_; + case DisplayName: + return invitees_[index.row()]->displayName_; + case AvatarUrl: + return invitees_[index.row()]->avatarUrl_; + default: + return {}; + } +} + +QStringList +InviteesModel::mxids() +{ + QStringList mxidList; + for (int i = 0; i < invitees_.length(); ++i) + mxidList.push_back(invitees_[i]->mxid_); + return mxidList; +} + +Invitee::Invitee(const QString &mxid, QObject *parent) + : QObject{parent} + , mxid_{mxid} +{ + http::client()->get_profile( + mxid_.toStdString(), + [this](const mtx::responses::Profile &res, mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to retrieve own profile info"); + return; + } + + displayName_ = QString::fromStdString(res.display_name); + avatarUrl_ = QString::fromStdString(res.avatar_url); + + emit userInfoLoaded(); + }); +} diff --git a/src/InviteesModel.h b/src/InviteesModel.h new file mode 100644 index 00000000..4bcc4e9d --- /dev/null +++ b/src/InviteesModel.h @@ -0,0 +1,56 @@ +#ifndef INVITEESMODEL_H +#define INVITEESMODEL_H + +#include +#include + +class Invitee : public QObject +{ + Q_OBJECT + +public: + Invitee(const QString &mxid, QObject *parent = nullptr); + +signals: + void userInfoLoaded(); + +private: + const QString mxid_; + QString displayName_; + QString avatarUrl_; + + friend class InviteesModel; +}; + +class InviteesModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles + { + Mxid, + DisplayName, + AvatarUrl, + }; + + InviteesModel(QObject *parent = nullptr); + + Q_INVOKABLE void addUser(QString mxid); + + QHash roleNames() const override; + int rowCount(const QModelIndex & = QModelIndex()) const override + { + return (int)invitees_.size(); + } + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QStringList mxids(); + +signals: + void accept(); + +private: + QVector invitees_; +}; + +#endif // INVITEESMODEL_H diff --git a/src/MemberList.cpp b/src/MemberList.cpp index 62488277..2a9c3fbc 100644 --- a/src/MemberList.cpp +++ b/src/MemberList.cpp @@ -43,7 +43,8 @@ MemberList::MemberList(const QString &room_id, QWidget *parent) void MemberList::addUsers(const std::vector &members) { - beginInsertRows(QModelIndex{}, m_memberList.count(), m_memberList.count() + members.size() - 1); + beginInsertRows( + QModelIndex{}, m_memberList.count(), m_memberList.count() + members.size() - 1); for (const auto &member : members) m_memberList.push_back( @@ -78,10 +79,11 @@ MemberList::data(const QModelIndex &index, int role) const } } -bool MemberList::canFetchMore(const QModelIndex &) const +bool +MemberList::canFetchMore(const QModelIndex &) const { - const size_t numMembers = rowCount(); - return (numMembers > 1 && numMembers < info_.member_count); + const size_t numMembers = rowCount(); + return (numMembers > 1 && numMembers < info_.member_count); } void diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 48d69493..2127801c 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -1076,6 +1076,16 @@ TimelineModel::openRoomSettings(QString room_id) openRoomSettingsDialog(settings); } +void +TimelineModel::openInviteUsers(QString room_id) +{ + InviteesModel *model = new InviteesModel{this}; + connect(model, &InviteesModel::accept, this, [this, model, room_id]() { + manager_->inviteUsers(room_id, model->mxids()); + }); + openInviteUsersDialog(model); +} + void TimelineModel::replyAction(QString id) { diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 5730fbab..e5189e61 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -17,6 +17,7 @@ #include "CacheStructs.h" #include "EventStore.h" #include "InputBar.h" +#include "InviteesModel.h" #include "MemberList.h" #include "Permissions.h" #include "ui/RoomSettings.h" @@ -239,6 +240,7 @@ public: Q_INVOKABLE void openUserProfile(QString userid); Q_INVOKABLE void openRoomMembers(); Q_INVOKABLE void openRoomSettings(QString room_id = QString()); + Q_INVOKABLE void openInviteUsers(QString room_id = QString()); Q_INVOKABLE void editAction(QString id); Q_INVOKABLE void replyAction(QString id); Q_INVOKABLE void readReceiptsAction(QString id) const; @@ -357,6 +359,7 @@ signals: void openRoomMembersDialog(MemberList *members); void openRoomSettingsDialog(RoomSettings *settings); + void openInviteUsersDialog(InviteesModel *invitees); void newMessageToSend(mtx::events::collections::TimelineEvents event); void addPendingMessageToStore(mtx::events::collections::TimelineEvents event); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 43b9a646..08b88efd 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -20,6 +20,7 @@ #include "DeviceVerificationFlow.h" #include "EventAccessors.h" #include "ImagePackModel.h" +#include "InviteesModel.h" #include "Logging.h" #include "MainWindow.h" #include "MatrixClient.h" @@ -184,6 +185,12 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par "Room Settings needs to be instantiated on the C++ side"); qmlRegisterUncreatableType( "im.nheko", 1, 0, "Room", "Room needs to be instantiated on the C++ side"); + qmlRegisterUncreatableType( + "im.nheko", + 1, + 0, + "InviteesModel", + "InviteesModel needs to be instantiated on the C++ side"); static auto self = this; qmlRegisterSingletonType( @@ -423,62 +430,13 @@ TimelineViewManager::openImageOverlayInternal(QString eventId, QImage img) }); } -void -TimelineViewManager::openInviteUsersDialog() -{ - MainWindow::instance()->openInviteUsersDialog( - [this](const QStringList &invitees) { emit inviteUsers(invitees); }); -} - -void -TimelineViewManager::openLink(QString link) const -{ - QUrl url(link); - if (url.scheme() == "https" && url.host() == "matrix.to") { - // handle matrix.to links internally - QString p = url.fragment(QUrl::FullyEncoded); - if (p.startsWith("/")) - p.remove(0, 1); - - auto temp = p.split("?"); - QString query; - if (temp.size() >= 2) - query = QUrl::fromPercentEncoding(temp.takeAt(1).toUtf8()); - - temp = temp.first().split("/"); - auto identifier = QUrl::fromPercentEncoding(temp.takeFirst().toUtf8()); - QString eventId = QUrl::fromPercentEncoding(temp.join('/').toUtf8()); - if (!identifier.isEmpty()) { - if (identifier.startsWith("@")) { - QByteArray uri = - "matrix:u/" + QUrl::toPercentEncoding(identifier.remove(0, 1)); - if (!query.isEmpty()) - uri.append("?" + query.toUtf8()); - ChatPage::instance()->handleMatrixUri(QUrl::fromEncoded(uri)); - } else if (identifier.startsWith("#")) { - QByteArray uri = - "matrix:r/" + QUrl::toPercentEncoding(identifier.remove(0, 1)); - if (!eventId.isEmpty()) - uri.append("/e/" + - QUrl::toPercentEncoding(eventId.remove(0, 1))); - if (!query.isEmpty()) - uri.append("?" + query.toUtf8()); - ChatPage::instance()->handleMatrixUri(QUrl::fromEncoded(uri)); - } else if (identifier.startsWith("!")) { - QByteArray uri = "matrix:roomid/" + - QUrl::toPercentEncoding(identifier.remove(0, 1)); - if (!eventId.isEmpty()) - uri.append("/e/" + - QUrl::toPercentEncoding(eventId.remove(0, 1))); - if (!query.isEmpty()) - uri.append("?" + query.toUtf8()); - ChatPage::instance()->handleMatrixUri(QUrl::fromEncoded(uri)); - } - } - } else { - QDesktopServices::openUrl(url); - } -} +//void +//TimelineViewManager::openInviteUsersDialog() +//{ + // TODO: move this somewhere where it will actually work (probably Rooms) +// MainWindow::instance()->openInviteUsersDialog( +// [this](const QStringList &invitees) { emit inviteUsers(invitees); }); +//} void TimelineViewManager::openLeaveRoomDialog(QString roomid) const From a76fc7d20058fa10e1345e0de0a1431a7161cd57 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 11 Jun 2021 18:53:32 -0400 Subject: [PATCH 04/56] Add a fancy loading spinner to the member list --- resources/qml/RoomMembers.qml | 7 +++++++ src/MemberList.cpp | 15 ++++++++++++--- src/MemberList.h | 4 ++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index d31fe319..8060a6cc 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -109,6 +109,13 @@ ApplicationWindow { } } } + + footer: BusyIndicator { + // This is not a wonderful solution, but it is the best way to calculate whether + // all users are loaded while keeping canFetchMore() const + running: members.numUsersLoaded < members.memberCount + anchors.centerIn: parent + } } } } diff --git a/src/MemberList.cpp b/src/MemberList.cpp index 2a9c3fbc..da4412d2 100644 --- a/src/MemberList.cpp +++ b/src/MemberList.cpp @@ -34,7 +34,10 @@ MemberList::MemberList(const QString &room_id, QWidget *parent) } try { - addUsers(cache::getMembers(room_id_.toStdString())); + auto members = cache::getMembers(room_id_.toStdString()); + addUsers(members); + numUsersLoaded_ = members.size(); + emit numUsersLoadedChanged(); } catch (const lmdb::error &e) { nhlog::db()->critical("Failed to retrieve members from cache: {}", e.what()); } @@ -83,11 +86,17 @@ bool MemberList::canFetchMore(const QModelIndex &) const { const size_t numMembers = rowCount(); - return (numMembers > 1 && numMembers < info_.member_count); + if (numMembers > 1 && numMembers < info_.member_count) + return true; + else + return false; } void MemberList::fetchMore(const QModelIndex &) { - addUsers(cache::getMembers(room_id_.toStdString(), rowCount())); + auto members = cache::getMembers(room_id_.toStdString(), rowCount()); + addUsers(members); + numUsersLoaded_ = members.size(); + emit numUsersLoadedChanged(); } diff --git a/src/MemberList.h b/src/MemberList.h index dbe69f4b..afc1a6e5 100644 --- a/src/MemberList.h +++ b/src/MemberList.h @@ -15,6 +15,7 @@ class MemberList : public QAbstractListModel Q_PROPERTY(size_t memberCount READ memberCount NOTIFY memberCountChanged) Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged) Q_PROPERTY(QString roomId READ roomId NOTIFY roomIdChanged) + Q_PROPERTY(int numUsersLoaded READ numUsersLoaded NOTIFY numUsersLoadedChanged) public: enum Roles @@ -37,12 +38,14 @@ public: size_t memberCount() const { return info_.member_count; } QString avatarUrl() const { return QString::fromStdString(info_.avatar_url); } QString roomId() const { return room_id_; } + int numUsersLoaded() const { return numUsersLoaded_; } signals: void roomNameChanged(); void memberCountChanged(); void avatarUrlChanged(); void roomIdChanged(); + void numUsersLoadedChanged(); public slots: void addUsers(const std::vector &users); @@ -55,4 +58,5 @@ private: QVector> m_memberList; QString room_id_; RoomInfo info_; + int numUsersLoaded_; }; From f0c88fc47435bda93a55271eb830bf80dc88443f Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 11 Jun 2021 19:18:04 -0400 Subject: [PATCH 05/56] Get member info loading working --- src/InviteesModel.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp index 849c5281..0081c7df 100644 --- a/src/InviteesModel.cpp +++ b/src/InviteesModel.cpp @@ -16,12 +16,10 @@ InviteesModel::addUser(QString mxid) auto invitee = new Invitee{mxid, this}; connect(invitee, &Invitee::userInfoLoaded, this, [this]() { - emit dataChanged(QModelIndex{}, QModelIndex{}); + endInsertRows(); }); invitees_.push_back(invitee); - - endInsertRows(); } QHash From a176de5f11f11bb3dad6cb8ea71a3b5edd6f27f4 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 11 Jun 2021 20:46:57 -0400 Subject: [PATCH 06/56] Make sure to use the default room id if none is specified --- resources/qml/InviteDialog.qml | 3 ++- src/timeline/TimelineModel.cpp | 6 +++--- src/timeline/TimelineModel.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index d5cc4c6d..278f772f 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -43,10 +43,11 @@ ApplicationWindow { RowLayout { spacing: 10 - TextField { + MatrixTextField { id: inviteeEntry placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.") + backgroundColor: colors.window Layout.fillWidth: true onAccepted: if (text !== "") addInvite() } diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 2127801c..ebbca6f4 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -1077,11 +1077,11 @@ TimelineModel::openRoomSettings(QString room_id) } void -TimelineModel::openInviteUsers(QString room_id) +TimelineModel::openInviteUsers(QString roomId) { InviteesModel *model = new InviteesModel{this}; - connect(model, &InviteesModel::accept, this, [this, model, room_id]() { - manager_->inviteUsers(room_id, model->mxids()); + connect(model, &InviteesModel::accept, this, [this, model, roomId]() { + manager_->inviteUsers(roomId == QString() ? room_id_ : roomId, model->mxids()); }); openInviteUsersDialog(model); } diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index e5189e61..b5144308 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -240,7 +240,7 @@ public: Q_INVOKABLE void openUserProfile(QString userid); Q_INVOKABLE void openRoomMembers(); Q_INVOKABLE void openRoomSettings(QString room_id = QString()); - Q_INVOKABLE void openInviteUsers(QString room_id = QString()); + Q_INVOKABLE void openInviteUsers(QString roomId = QString()); Q_INVOKABLE void editAction(QString id); Q_INVOKABLE void replyAction(QString id); Q_INVOKABLE void readReceiptsAction(QString id) const; From 3c999ade95785987d2213036067110bc1bb4fcb1 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 11 Jun 2021 20:47:43 -0400 Subject: [PATCH 07/56] Focus the input bar automatically --- resources/qml/InviteDialog.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 278f772f..002a35c2 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -50,6 +50,7 @@ ApplicationWindow { backgroundColor: colors.window Layout.fillWidth: true onAccepted: if (text !== "") addInvite() + Component.onCompleted: forceActiveFocus() } Button { From 4746fcd16f458c9b88e5e50074c33254da7e1141 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 11 Jun 2021 20:48:20 -0400 Subject: [PATCH 08/56] Add fancy label if you enter a bad mxid --- resources/qml/InviteDialog.qml | 73 ++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 002a35c2..9ace3246 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -16,6 +16,10 @@ ApplicationWindow { invitees.addUser(inviteeEntry.text); inviteeEntry.clear(); } + else + { + warningLabel.show() + } } title: qsTr("Invite users to ") + roomName @@ -51,6 +55,11 @@ ApplicationWindow { Layout.fillWidth: true onAccepted: if (text !== "") addInvite() Component.onCompleted: forceActiveFocus() + + Shortcut { + sequence: "Ctrl+Enter" + onActivated: inviteDialogRoot.accept() + } } Button { @@ -59,6 +68,70 @@ ApplicationWindow { } } + Label { + id: warningLabel + + function show() { + state = "shown"; + warningLabelTimer.start(); + } + + text: qsTr("Please enter a valid username (e.g. @joe:matrix.org).") + color: "red" + visible: false + opacity: 0 + state: "hidden" + + states: [ + State { + name: "shown" + PropertyChanges { + target: warningLabel + opacity: 1 + visible: true + } + }, + State { + name: "hidden" + PropertyChanges { + target: warningLabel + opacity: 0 + visible: false + } + } + ] + + transitions: [ + Transition { + from: "shown" + to: "hidden" + reversible: true + + SequentialAnimation { + NumberAnimation { + target: warningLabel + property: "opacity" + duration: 500 + } + + PropertyAction { + target: warningLabel + property: "visible" + } + } + } + ] + + Timer { + id: warningLabelTimer + + interval: 2000 + repeat: false + running: false + onTriggered: warningLabel.state = "hidden" + } + } + ListView { id: inviteesList From c566a6254138037d8916bedf9660a66f2d65187f Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 11 Jun 2021 20:48:28 -0400 Subject: [PATCH 09/56] Clean up code --- resources/qml/InviteDialog.qml | 13 ------------- src/InviteesModel.cpp | 4 +--- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 9ace3246..d8e176e6 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -172,19 +172,6 @@ ApplicationWindow { } } } -// delegate: RowLayout { -// spacing: 10 - -// Avatar { -// url: model.avatarUrl -// width: 20 -// height: width -// } - -// Label { -// text: model.displayName + " (" + model.mxid + ")" -// } -// } } } diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp index 0081c7df..1da7baf4 100644 --- a/src/InviteesModel.cpp +++ b/src/InviteesModel.cpp @@ -15,9 +15,7 @@ InviteesModel::addUser(QString mxid) beginInsertRows(QModelIndex(), invitees_.count(), invitees_.count()); auto invitee = new Invitee{mxid, this}; - connect(invitee, &Invitee::userInfoLoaded, this, [this]() { - endInsertRows(); - }); + connect(invitee, &Invitee::userInfoLoaded, this, [this]() { endInsertRows(); }); invitees_.push_back(invitee); } From 03acced6d6c5b95c2272f8d3036a6295d048a4f5 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 11 Jun 2021 21:04:35 -0400 Subject: [PATCH 10/56] Add close on Escape shortcut --- resources/qml/InviteDialog.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index d8e176e6..3470a7f1 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -34,6 +34,11 @@ ApplicationWindow { onActivated: inviteDialogRoot.accept() } + Shortcut { + sequence: StandardKey.Cancel + onActivated: inviteDialogRoot.close() + } + ColumnLayout { anchors.fill: parent anchors.margins: 10 From 908629bec0ff490b42aa1f0b1c8fade160606e96 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 11 Jun 2021 21:05:18 -0400 Subject: [PATCH 11/56] Fix item that accept() is called on --- resources/qml/InviteDialog.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 3470a7f1..8ba0f262 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -31,7 +31,7 @@ ApplicationWindow { // TODO: make this work in the TextField Shortcut { sequence: "Ctrl+Enter" - onActivated: inviteDialogRoot.accept() + onActivated: invitees.accept() } Shortcut { @@ -63,7 +63,7 @@ ApplicationWindow { Shortcut { sequence: "Ctrl+Enter" - onActivated: inviteDialogRoot.accept() + onActivated: invitees.accept() } } From 59a2630be74898c5d8c13dbf0489e5caa94063fd Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 12 Jun 2021 12:00:59 -0400 Subject: [PATCH 12/56] Simplify room details access This removes the redundant room name property --- resources/qml/InviteDialog.qml | 1 - resources/qml/RoomMembers.qml | 5 ++--- resources/qml/TopBar.qml | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 8ba0f262..2932e398 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -56,7 +56,6 @@ ApplicationWindow { id: inviteeEntry placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.") - backgroundColor: colors.window Layout.fillWidth: true onAccepted: if (text !== "") addInvite() Component.onCompleted: forceActiveFocus() diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 8060a6cc..8addd704 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -7,10 +7,9 @@ import im.nheko 1.0 ApplicationWindow { id: roomMembersRoot - property string roomName: Rooms.currentRoom.roomName property MemberList members - title: qsTr("Members of ") + roomName + title: qsTr("Members of ") + members.roomName x: MainWindow.x + (MainWindow.width / 2) - (width / 2) y: MainWindow.y + (MainWindow.height / 2) - (height / 2) height: 650 @@ -40,7 +39,7 @@ ApplicationWindow { Label { font.pixelSize: 24 - text: members.memberCount + (members.memberCount === 1 ? qsTr(" person in ") : qsTr(" people in ")) + roomName + text: members.memberCount + (members.memberCount === 1 ? qsTr(" person in ") : qsTr(" people in ")) + members.roomName Layout.alignment: Qt.AlignHCenter } diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml index 6cf747c5..aa5c5d18 100644 --- a/resources/qml/TopBar.qml +++ b/resources/qml/TopBar.qml @@ -111,12 +111,12 @@ Rectangle { Platform.MenuItem { visible: room ? room.permissions.canInvite() : false text: qsTr("Invite users") - onTriggered: TimelineManager.timeline.openInviteUsers() + onTriggered: Rooms.currentRoom.openInviteUsers() } Platform.MenuItem { text: qsTr("Members") - onTriggered: Rooms.currentRoom.openRoomMembers(room.roomId()) + onTriggered: Rooms.currentRoom.openRoomMembers() } Platform.MenuItem { From 60b3c34d78120d10114fc14600b2b142bbc80362 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 12 Jun 2021 13:17:31 -0400 Subject: [PATCH 13/56] Permissions only needs a roomid to function --- src/timeline/Permissions.cpp | 6 +++--- src/timeline/Permissions.h | 4 ++-- src/timeline/TimelineModel.cpp | 1 + src/timeline/TimelineModel.h | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/timeline/Permissions.cpp b/src/timeline/Permissions.cpp index 1eaab468..e4957045 100644 --- a/src/timeline/Permissions.cpp +++ b/src/timeline/Permissions.cpp @@ -8,9 +8,9 @@ #include "MatrixClient.h" #include "TimelineModel.h" -Permissions::Permissions(TimelineModel *parent) +Permissions::Permissions(QString roomId, QObject *parent) : QObject(parent) - , room(parent) + , roomId_(roomId) { invalidate(); } @@ -19,7 +19,7 @@ void Permissions::invalidate() { pl = cache::client() - ->getStateEvent(room->roomId().toStdString()) + ->getStateEvent(roomId_.toStdString()) .value_or(mtx::events::StateEvent{}) .content; } diff --git a/src/timeline/Permissions.h b/src/timeline/Permissions.h index f7e6f389..7aab1ddb 100644 --- a/src/timeline/Permissions.h +++ b/src/timeline/Permissions.h @@ -15,7 +15,7 @@ class Permissions : public QObject Q_OBJECT public: - Permissions(TimelineModel *parent); + Permissions(QString roomId, QObject *parent = nullptr); Q_INVOKABLE bool canInvite(); Q_INVOKABLE bool canBan(); @@ -28,6 +28,6 @@ public: void invalidate(); private: - TimelineModel *room; + QString roomId_; mtx::events::state::PowerLevels pl; }; diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index ebbca6f4..516a499b 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -318,6 +318,7 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj , events(room_id.toStdString(), this) , room_id_(room_id) , manager_(manager) + , permissions_{room_id} { lastMessage_.timestamp = 0; diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index b5144308..ebf24bec 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -396,7 +396,7 @@ private: TimelineViewManager *manager_; InputBar input_{this}; - Permissions permissions_{this}; + Permissions permissions_; QTimer showEventTimer{this}; QString eventIdToShow; From db8af24bea5b46b76c0afd90813a4a29e13eea28 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 12 Jun 2021 22:00:14 -0400 Subject: [PATCH 14/56] Don't emit signal in constructor --- src/MemberList.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/MemberList.cpp b/src/MemberList.cpp index da4412d2..7f4dfa99 100644 --- a/src/MemberList.cpp +++ b/src/MemberList.cpp @@ -37,7 +37,6 @@ MemberList::MemberList(const QString &room_id, QWidget *parent) auto members = cache::getMembers(room_id_.toStdString()); addUsers(members); numUsersLoaded_ = members.size(); - emit numUsersLoadedChanged(); } catch (const lmdb::error &e) { nhlog::db()->critical("Failed to retrieve members from cache: {}", e.what()); } From 182de32380d192a6dcf71b656e5e5cd3bab30a6e Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 15 Jun 2021 12:32:00 -0400 Subject: [PATCH 15/56] Use standard buttons for OK button --- resources/qml/RoomSettings.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/qml/RoomSettings.qml b/resources/qml/RoomSettings.qml index c852b837..a27be13e 100644 --- a/resources/qml/RoomSettings.qml +++ b/resources/qml/RoomSettings.qml @@ -280,10 +280,10 @@ ApplicationWindow { } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("OK") - onClicked: close() + DialogButtonBox { + Layout.fillWidth: true + standardButtons: DialogButtonBox.Ok + onAccepted: close() } } From d2d5229ede5124ba6cf9e85790dcd564faad00db Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 13:31:38 -0400 Subject: [PATCH 16/56] make lint --- resources/qml/InviteDialog.qml | 44 +++++++++++++++++++++------- resources/qml/RoomMembers.qml | 4 +++ resources/qml/TimelineView.qml | 1 + src/ChatPage.cpp | 43 ++++++++++++++------------- src/InviteesModel.cpp | 4 +++ src/InviteesModel.h | 4 +++ src/timeline/TimelineViewManager.cpp | 6 ++-- 7 files changed, 72 insertions(+), 34 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 2932e398..ae74d3da 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 @@ -11,14 +15,11 @@ ApplicationWindow { property InviteesModel invitees function addInvite() { - if (inviteeEntry.text.match("@.+?:.{3,}")) - { + if (inviteeEntry.text.match("@.+?:.{3,}")) { invitees.addUser(inviteeEntry.text); inviteeEntry.clear(); - } - else - { - warningLabel.show() + } else { + warningLabel.show(); } } @@ -57,19 +58,29 @@ ApplicationWindow { placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.") Layout.fillWidth: true - onAccepted: if (text !== "") addInvite() + onAccepted: { + if (text !== "") { + addInvite(); + } + } Component.onCompleted: forceActiveFocus() Shortcut { sequence: "Ctrl+Enter" onActivated: invitees.accept() } + } Button { text: qsTr("Add") - onClicked: if (inviteeEntry.text !== "") addInvite() + onClicked: { + if (inviteeEntry.text !== "") { + addInvite(); + } + } } + } Label { @@ -85,26 +96,28 @@ ApplicationWindow { visible: false opacity: 0 state: "hidden" - states: [ State { name: "shown" + PropertyChanges { target: warningLabel opacity: 1 visible: true } + }, State { name: "hidden" + PropertyChanges { target: warningLabel opacity: 0 visible: false } + } ] - transitions: [ Transition { from: "shown" @@ -122,7 +135,9 @@ ApplicationWindow { target: warningLabel property: "visible" } + } + } ] @@ -134,6 +149,7 @@ ApplicationWindow { running: false onTriggered: warningLabel.state = "hidden" } + } ListView { @@ -174,9 +190,13 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true } + } + } + } + } footer: DialogButtonBox { @@ -194,7 +214,9 @@ ApplicationWindow { Button { text: qsTr("Cancel") DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole - onClicked: inviteDialogRoot.close(); + onClicked: inviteDialogRoot.close() } + } + } diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 8addd704..44b917b1 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index d515b9b4..148a5817 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -157,6 +157,7 @@ Item { Layout.alignment: Qt.AlignHCenter enabled: false } + MatrixText { text: parent.roomName font.pixelSize: 24 diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 8b4cfeef..70fd32fd 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -117,29 +117,32 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect(this, &ChatPage::loggedOut, this, &ChatPage::logout); // TODO: once this signal is moved, reenable this -// connect(view_manager_, &TimelineViewManager::inviteUsers, this, [this](QStringList users) { -// const auto room_id = currentRoom().toStdString(); + // connect(view_manager_, &TimelineViewManager::inviteUsers, this, [this](QStringList + // users) { + // const auto room_id = currentRoom().toStdString(); -// for (int ii = 0; ii < users.size(); ++ii) { -// QTimer::singleShot(ii * 500, this, [this, room_id, ii, users]() { -// const auto user = users.at(ii); + // for (int ii = 0; ii < users.size(); ++ii) { + // QTimer::singleShot(ii * 500, this, [this, room_id, ii, users]() { + // const auto user = users.at(ii); -// http::client()->invite_user( -// room_id, -// user.toStdString(), -// [this, user](const mtx::responses::RoomInvite &, -// mtx::http::RequestErr err) { -// if (err) { -// emit showNotification( -// tr("Failed to invite user: %1").arg(user)); -// return; -// } + // http::client()->invite_user( + // room_id, + // user.toStdString(), + // [this, user](const mtx::responses::RoomInvite &, + // mtx::http::RequestErr err) { + // if (err) { + // emit showNotification( + // tr("Failed to invite user: + // %1").arg(user)); + // return; + // } -// emit showNotification(tr("Invited user: %1").arg(user)); -// }); -// }); -// } -// }); + // emit showNotification(tr("Invited user: + // %1").arg(user)); + // }); + // }); + // } + // }); connect( view_manager_, diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp index 1da7baf4..59054690 100644 --- a/src/InviteesModel.cpp +++ b/src/InviteesModel.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "InviteesModel.h" #include "Cache.h" diff --git a/src/InviteesModel.h b/src/InviteesModel.h index 4bcc4e9d..ac9208a0 100644 --- a/src/InviteesModel.h +++ b/src/InviteesModel.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #ifndef INVITEESMODEL_H #define INVITEESMODEL_H diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 08b88efd..8daa2124 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -430,10 +430,10 @@ TimelineViewManager::openImageOverlayInternal(QString eventId, QImage img) }); } -//void -//TimelineViewManager::openInviteUsersDialog() +// void +// TimelineViewManager::openInviteUsersDialog() //{ - // TODO: move this somewhere where it will actually work (probably Rooms) +// TODO: move this somewhere where it will actually work (probably Rooms) // MainWindow::instance()->openInviteUsersDialog( // [this](const QStringList &invitees) { emit inviteUsers(invitees); }); //} From 913d0fd13985712f48ee50dcdd46618606364f91 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 15:52:10 -0400 Subject: [PATCH 17/56] Make macOS CI happy (again) --- src/MemberList.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MemberList.h b/src/MemberList.h index afc1a6e5..80ab834c 100644 --- a/src/MemberList.h +++ b/src/MemberList.h @@ -51,8 +51,8 @@ public slots: void addUsers(const std::vector &users); protected: - bool canFetchMore(const QModelIndex &) const; - void fetchMore(const QModelIndex &); + bool canFetchMore(const QModelIndex &) const override; + void fetchMore(const QModelIndex &) override; private: QVector> m_memberList; From 4ddcff2300ba37c88d8170f063ba1f0ae546efc8 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 16:16:02 -0400 Subject: [PATCH 18/56] Fix borked property stuff --- resources/qml/RoomList.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index a1ce8d7e..7c03673f 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -133,7 +133,7 @@ Page { states: [ State { name: "highlight" - when: hovered.hovered && !((Rooms.currentRoom && roomId == Rooms.currentRoom.roomId()) || Rooms.currentRoomPreview.roomid == roomId) + when: hovered.hovered && !((Rooms.currentRoom && roomId == Rooms.currentRoom.roomId) || Rooms.currentRoomPreview.roomid == roomId) PropertyChanges { target: roomItem @@ -147,7 +147,7 @@ Page { }, State { name: "selected" - when: (Rooms.currentRoom && roomId == Rooms.currentRoom.roomId()) || Rooms.currentRoomPreview.roomid == roomId + when: (Rooms.currentRoom && roomId == Rooms.currentRoom.roomId) || Rooms.currentRoomPreview.roomid == roomId PropertyChanges { target: roomItem From 5d9556722f409c9202c0b506dd789b7d2d722b01 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 16:16:18 -0400 Subject: [PATCH 19/56] Fix up components --- resources/qml/Root.qml | 49 ++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml index ecd0bdb7..3825fb89 100644 --- a/resources/qml/Root.qml +++ b/resources/qml/Root.qml @@ -47,6 +47,13 @@ Page { } + Component { + id: roomMembersComponent + + RoomMembers { + } + } + Component { id: mobileCallInviteDialog @@ -63,6 +70,20 @@ Page { } + Component { + id: deviceVerificationDialog + + DeviceVerification { + } + + } + Component { + id: inviteDialog + + InviteDialog { + } + } + Shortcut { sequence: "Ctrl+K" onActivated: { @@ -82,20 +103,6 @@ Page { onActivated: Rooms.previousRoom() } - Component { - id: deviceVerificationDialog - - DeviceVerification { - } - - } - Component { - id: inviteDialog - - InviteDialog { - } - } - Connections { target: TimelineManager onNewDeviceVerificationRequest: { @@ -123,18 +130,18 @@ Page { } Connections { - target: TimelineManager.timeline + target: Rooms.currentRoom onOpenRoomMembersDialog: { var membersDialog = roomMembersComponent.createObject(timelineRoot, { "members": members, - "roomName": TimelineManager.timeline.roomName + "roomName": Rooms.currentRoom.roomName }); membersDialog.show(); } } Connections { - target: TimelineManager.timeline + target: Rooms.currentRoom onOpenRoomSettingsDialog: { var roomSettings = roomSettingsComponent.createObject(timelineRoot, { "roomSettings": settings @@ -144,12 +151,12 @@ Page { } Connections { - target: TimelineManager.timeline + target: Rooms.currentRoom onOpenInviteUsersDialog: { var dialog = inviteDialog.createObject(timelineRoot, { - "roomId": TimelineManager.timeline.roomId, - "roomName": TimelineManager.timeline.roomName - }); + "roomId": Rooms.currentRoom.roomId, + "roomName": Rooms.currentRoom.roomName + }); dialog.show(); } } From 02326fce70372e2dc8753ba51286b24f93882559 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 16:17:05 -0400 Subject: [PATCH 20/56] Fix background color on text input --- resources/qml/InviteDialog.qml | 1 + resources/qml/MatrixTextField.qml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index ae74d3da..e171808e 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -56,6 +56,7 @@ ApplicationWindow { MatrixTextField { id: inviteeEntry + backgroundColor: Nheko.colors.window placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.") Layout.fillWidth: true onAccepted: { diff --git a/resources/qml/MatrixTextField.qml b/resources/qml/MatrixTextField.qml index 3c660bac..80732b27 100644 --- a/resources/qml/MatrixTextField.qml +++ b/resources/qml/MatrixTextField.qml @@ -10,6 +10,8 @@ import im.nheko 1.0 TextField { id: input + property alias backgroundColor: backgroundRect.color + palette: Nheko.colors color: Nheko.colors.text @@ -62,6 +64,8 @@ TextField { } background: Rectangle { + id: backgroundRect + color: Nheko.colors.base } From cb8d1401234b2a0f94a2e778100e0b2f757c45a2 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 16:48:06 -0400 Subject: [PATCH 21/56] Fix properties --- resources/qml/RoomMembers.qml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 44b917b1..9b920f2b 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -7,6 +7,7 @@ import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import QtQuick.Window 2.12 import im.nheko 1.0 +import "./ui" ApplicationWindow { id: roomMembersRoot @@ -38,7 +39,7 @@ ApplicationWindow { displayName: members.roomName Layout.alignment: Qt.AlignHCenter url: members.avatarUrl.replace("mxc://", "image://MxcImage/") - onClicked: TimelineManager.timeline.openRoomSettings(members.roomId) + onClicked: Rooms.currentRoom.openRoomSettings(members.roomId) } Label { @@ -53,12 +54,12 @@ ApplicationWindow { hoverEnabled: true ToolTip.visible: hovered ToolTip.text: qsTr("Invite more people") - onClicked: TimelineManager.timeline.openInviteUsersDialog() + onClicked: Rooms.currentRoom.openInviteUsersDialog() } ScrollView { clip: false - palette: colors + palette: Nheko.colors padding: 10 ScrollBar.horizontal.visible: false Layout.fillHeight: true @@ -83,12 +84,12 @@ ApplicationWindow { spacing: 10 Avatar { - width: avatarSize - height: avatarSize + width: Nheko.avatarSize + height: Nheko.avatarSize userid: model.mxid url: model.avatarUrl.replace("mxc://", "image://MxcImage/") displayName: model.displayName - onClicked: TimelineManager.timeline.openUserProfile(model.mxid) + onClicked: Rooms.currentRoom.openUserProfile(model.mxid) } ColumnLayout { @@ -96,13 +97,13 @@ ApplicationWindow { Label { text: model.displayName - color: TimelineManager.userColor(model ? model.mxid : "", colors.window) + color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window) font.pointSize: 12 } Label { text: model.mxid - color: colors.buttonText + color: Nheko.colors.buttonText font.pointSize: 10 } From 3c5b395171ae526c4564fad5182c174ff2352b65 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 16:48:19 -0400 Subject: [PATCH 22/56] Use fancy spinner (courtesy of redsky) --- resources/qml/RoomMembers.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 9b920f2b..d1d60a16 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -114,7 +114,7 @@ ApplicationWindow { } } - footer: BusyIndicator { + footer: Spinner { // This is not a wonderful solution, but it is the best way to calculate whether // all users are loaded while keeping canFetchMore() const running: members.numUsersLoaded < members.memberCount From baa9dfe110698a741eedc6209e33a4db687dffbe Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 16:49:34 -0400 Subject: [PATCH 23/56] Clean up code --- src/ChatPage.cpp | 28 ---------------------------- src/timeline/TimelineModel.cpp | 6 +++--- src/timeline/TimelineViewManager.cpp | 8 -------- 3 files changed, 3 insertions(+), 39 deletions(-) diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 70fd32fd..6b8c1e10 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -116,34 +116,6 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect(this, &ChatPage::loggedOut, this, &ChatPage::logout); - // TODO: once this signal is moved, reenable this - // connect(view_manager_, &TimelineViewManager::inviteUsers, this, [this](QStringList - // users) { - // const auto room_id = currentRoom().toStdString(); - - // for (int ii = 0; ii < users.size(); ++ii) { - // QTimer::singleShot(ii * 500, this, [this, room_id, ii, users]() { - // const auto user = users.at(ii); - - // http::client()->invite_user( - // room_id, - // user.toStdString(), - // [this, user](const mtx::responses::RoomInvite &, - // mtx::http::RequestErr err) { - // if (err) { - // emit showNotification( - // tr("Failed to invite user: - // %1").arg(user)); - // return; - // } - - // emit showNotification(tr("Invited user: - // %1").arg(user)); - // }); - // }); - // } - // }); - connect( view_manager_, &TimelineViewManager::inviteUsers, diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 516a499b..7ce0e98a 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -1074,7 +1074,7 @@ TimelineModel::openRoomSettings(QString room_id) { RoomSettings *settings = new RoomSettings(room_id == QString() ? roomId() : room_id, this); connect(this, &TimelineModel::roomAvatarUrlChanged, settings, &RoomSettings::avatarChanged); - openRoomSettingsDialog(settings); + emit openRoomSettingsDialog(settings); } void @@ -1082,9 +1082,9 @@ TimelineModel::openInviteUsers(QString roomId) { InviteesModel *model = new InviteesModel{this}; connect(model, &InviteesModel::accept, this, [this, model, roomId]() { - manager_->inviteUsers(roomId == QString() ? room_id_ : roomId, model->mxids()); + emit manager_->inviteUsers(roomId == QString() ? room_id_ : roomId, model->mxids()); }); - openInviteUsersDialog(model); + emit openInviteUsersDialog(model); } void diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 8daa2124..64493e5b 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -430,14 +430,6 @@ TimelineViewManager::openImageOverlayInternal(QString eventId, QImage img) }); } -// void -// TimelineViewManager::openInviteUsersDialog() -//{ -// TODO: move this somewhere where it will actually work (probably Rooms) -// MainWindow::instance()->openInviteUsersDialog( -// [this](const QStringList &invitees) { emit inviteUsers(invitees); }); -//} - void TimelineViewManager::openLeaveRoomDialog(QString roomid) const { From 81a3faee7b7035501325a44a04eba2fbfde8a7e6 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 16:56:56 -0400 Subject: [PATCH 24/56] Finish converting function to property --- resources/qml/Completer.qml | 2 +- resources/qml/RoomList.qml | 4 ++-- resources/qml/TimelineView.qml | 2 +- resources/qml/TopBar.qml | 2 +- resources/qml/delegates/MessageDelegate.qml | 2 +- resources/qml/voip/PlaceCall.qml | 4 ++-- resources/qml/voip/ScreenShare.qml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/resources/qml/Completer.qml b/resources/qml/Completer.qml index 333fb11d..00fc3216 100644 --- a/resources/qml/Completer.qml +++ b/resources/qml/Completer.qml @@ -70,7 +70,7 @@ Popup { onCompleterNameChanged: { if (completerName) { if (completerName == "user") - completer = TimelineManager.completerFor(completerName, room.roomId()); + completer = TimelineManager.completerFor(completerName, room.roomId); else completer = TimelineManager.completerFor(completerName); completer.setSearchString(""); diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index 7c03673f..9dac5830 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -33,8 +33,8 @@ Page { Connections { onActiveTimelineChanged: { - roomlist.positionViewAtIndex(Rooms.roomidToIndex(Rooms.currentRoom.roomId()), ListView.Contain); - console.log("Test" + Rooms.currentRoom.roomId() + " " + Rooms.roomidToIndex(Rooms.currentRoom.roomId())); + roomlist.positionViewAtIndex(Rooms.roomidToIndex(Rooms.currentRoom.roomId), ListView.Contain); + console.log("Test" + Rooms.currentRoom.roomId + " " + Rooms.roomidToIndex(Rooms.currentRoom.roomId)); } target: TimelineManager } diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 148a5817..f5979e14 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -246,7 +246,7 @@ Item { NhekoDropArea { anchors.fill: parent - roomid: room ? room.roomId() : "" + roomid: room ? room.roomId : "" } Connections { diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml index aa5c5d18..48491f84 100644 --- a/resources/qml/TopBar.qml +++ b/resources/qml/TopBar.qml @@ -121,7 +121,7 @@ Rectangle { Platform.MenuItem { text: qsTr("Leave room") - onTriggered: TimelineManager.openLeaveRoomDialog(room.roomId()) + onTriggered: TimelineManager.openLeaveRoomDialog(room.roomId) } Platform.MenuItem { diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml index c64ae887..a98c2a8b 100644 --- a/resources/qml/delegates/MessageDelegate.qml +++ b/resources/qml/delegates/MessageDelegate.qml @@ -232,7 +232,7 @@ Item { body: formatted isOnlyEmoji: false isReply: d.isReply - formatted: qsTr("%1 created and configured room: %2").arg(d.userName).arg(room.roomId()) + formatted: qsTr("%1 created and configured room: %2").arg(d.userName).arg(room.roomId) } } diff --git a/resources/qml/voip/PlaceCall.qml b/resources/qml/voip/PlaceCall.qml index 5f564853..97932cc9 100644 --- a/resources/qml/voip/PlaceCall.qml +++ b/resources/qml/voip/PlaceCall.qml @@ -88,7 +88,7 @@ Popup { onClicked: { if (buttonLayout.validateMic()) { Settings.microphone = micCombo.currentText; - CallManager.sendInvite(room.roomId(), CallType.VOICE); + CallManager.sendInvite(room.roomId, CallType.VOICE); close(); } } @@ -102,7 +102,7 @@ Popup { if (buttonLayout.validateMic()) { Settings.microphone = micCombo.currentText; Settings.camera = cameraCombo.currentText; - CallManager.sendInvite(room.roomId(), CallType.VIDEO); + CallManager.sendInvite(room.roomId, CallType.VIDEO); close(); } } diff --git a/resources/qml/voip/ScreenShare.qml b/resources/qml/voip/ScreenShare.qml index a10057b2..8cd43b1c 100644 --- a/resources/qml/voip/ScreenShare.qml +++ b/resources/qml/voip/ScreenShare.qml @@ -136,7 +136,7 @@ Popup { Settings.screenSharePiP = pipCheckBox.checked; Settings.screenShareRemoteVideo = remoteVideoCheckBox.checked; Settings.screenShareHideCursor = hideCursorCheckBox.checked; - CallManager.sendInvite(room.roomId(), CallType.SCREEN, windowCombo.currentIndex); + CallManager.sendInvite(room.roomId, CallType.SCREEN, windowCombo.currentIndex); close(); } } From 462204f3f462a5f348227379b5743405df38ff23 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 18:23:03 -0400 Subject: [PATCH 25/56] Fix properties --- resources/qml/InviteDialog.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index e171808e..83471658 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -164,8 +164,8 @@ ApplicationWindow { spacing: 10 Avatar { - width: avatarSize - height: avatarSize + width: Nheko.avatarsize + height: Nheko.avatarsize userid: model.mxid url: model.avatarUrl.replace("mxc://", "image://MxcImage/") displayName: model.displayName @@ -177,13 +177,13 @@ ApplicationWindow { Label { text: model.displayName - color: TimelineManager.userColor(model ? model.mxid : "", colors.window) + color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window) font.pointSize: 12 } Label { text: model.mxid - color: colors.buttonText + color: Nheko.colors.buttonText font.pointSize: 10 } From 73d902611d583e6b57319bfbbf5071e7967a4ff0 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 18:23:21 -0400 Subject: [PATCH 26/56] Actually set invitees property --- resources/qml/Root.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml index 3825fb89..31d00585 100644 --- a/resources/qml/Root.qml +++ b/resources/qml/Root.qml @@ -155,7 +155,8 @@ Page { onOpenInviteUsersDialog: { var dialog = inviteDialog.createObject(timelineRoot, { "roomId": Rooms.currentRoom.roomId, - "roomName": Rooms.currentRoom.roomName + "roomName": Rooms.currentRoom.roomName, + "invitees": invitees }); dialog.show(); } From f30c3db86058f2700a1e2331bb1167d2ebf88f4d Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 18:24:04 -0400 Subject: [PATCH 27/56] Fix loaded member count --- src/MemberList.cpp | 2 +- src/MemberList.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MemberList.cpp b/src/MemberList.cpp index 7f4dfa99..b2ed82bd 100644 --- a/src/MemberList.cpp +++ b/src/MemberList.cpp @@ -96,6 +96,6 @@ MemberList::fetchMore(const QModelIndex &) { auto members = cache::getMembers(room_id_.toStdString(), rowCount()); addUsers(members); - numUsersLoaded_ = members.size(); + numUsersLoaded_ += members.size(); emit numUsersLoadedChanged(); } diff --git a/src/MemberList.h b/src/MemberList.h index 80ab834c..aa240b7a 100644 --- a/src/MemberList.h +++ b/src/MemberList.h @@ -58,5 +58,5 @@ private: QVector> m_memberList; QString room_id_; RoomInfo info_; - int numUsersLoaded_; + int numUsersLoaded_{0}; }; From 155315ecbb84cf517d1daa98c76ea7eb41741086 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 18:24:38 -0400 Subject: [PATCH 28/56] Fix Ctrl-Enter shortcut --- resources/qml/InviteDialog.qml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 83471658..3415a93e 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -29,11 +29,9 @@ ApplicationWindow { height: 380 width: 340 - // TODO: make this work in the TextField - Shortcut { - sequence: "Ctrl+Enter" - onActivated: invitees.accept() - } + Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && event.modifiers & Qt.ControlModifier) + Keys.onEnterPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() + Keys.onReturnPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() Shortcut { sequence: StandardKey.Cancel @@ -66,10 +64,14 @@ ApplicationWindow { } Component.onCompleted: forceActiveFocus() - Shortcut { - sequence: "Ctrl+Enter" - onActivated: invitees.accept() - } +// Shortcut { +// sequence: "Ctrl+Enter" +// onActivated: invitees.accept() +// } + + Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && event.modifiers & Qt.ControlModifier) + Keys.onEnterPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() + Keys.onReturnPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() } From d2c62529117e878cb92b59039aa367909e4c3871 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 19:24:45 -0400 Subject: [PATCH 29/56] More shortcut stuff --- resources/qml/InviteDialog.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 3415a93e..23420fa2 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -29,10 +29,6 @@ ApplicationWindow { height: 380 width: 340 - Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && event.modifiers & Qt.ControlModifier) - Keys.onEnterPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() - Keys.onReturnPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() - Shortcut { sequence: StandardKey.Cancel onActivated: inviteDialogRoot.close() @@ -43,6 +39,10 @@ ApplicationWindow { anchors.margins: 10 spacing: 10 + Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier)) + Keys.onEnterPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() + Keys.onReturnPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() + Label { text: qsTr("User ID to invite") Layout.fillWidth: true @@ -69,7 +69,7 @@ ApplicationWindow { // onActivated: invitees.accept() // } - Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && event.modifiers & Qt.ControlModifier) + Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier)) Keys.onEnterPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() Keys.onReturnPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() From 4d5950b6a7a38327ce1be88a910a474caead68b5 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 19:25:09 -0400 Subject: [PATCH 30/56] Document bad behavior with footer and spinner --- resources/qml/RoomMembers.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index d1d60a16..da650859 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -117,7 +117,10 @@ ApplicationWindow { footer: Spinner { // This is not a wonderful solution, but it is the best way to calculate whether // all users are loaded while keeping canFetchMore() const - running: members.numUsersLoaded < members.memberCount + + // TODO: just toggling the visiblity leaves some large empty space at the bottom + // of the list. This should be fixed. + visible: members.numUsersLoaded < members.memberCount anchors.centerIn: parent } } From 67fff656b3bc13d4264dc144582699931953ffb3 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 19:43:07 -0400 Subject: [PATCH 31/56] Fix bad property name --- resources/qml/InviteDialog.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 23420fa2..8bdbb767 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -166,8 +166,8 @@ ApplicationWindow { spacing: 10 Avatar { - width: Nheko.avatarsize - height: Nheko.avatarsize + width: Nheko.avatarSize + height: Nheko.avatarSize userid: model.mxid url: model.avatarUrl.replace("mxc://", "image://MxcImage/") displayName: model.displayName From f1f5796fb8ba5b504043310dba17acd3ef2860bd Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 17 Jul 2021 19:43:35 -0400 Subject: [PATCH 32/56] Get Ctrl+Enter working and fix cleaning up and closing --- resources/qml/InviteDialog.qml | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 8bdbb767..417de049 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -23,12 +23,24 @@ ApplicationWindow { } } + function cleanUpAndClose() { + if (inviteeEntry.text !== "") + addInvite(); + invitees.accept(); + close(); + } + title: qsTr("Invite users to ") + roomName x: MainWindow.x + (MainWindow.width / 2) - (width / 2) y: MainWindow.y + (MainWindow.height / 2) - (height / 2) height: 380 width: 340 + Shortcut { + sequence: "Ctrl+Enter" + onActivated: cleanUpAndClose() + } + Shortcut { sequence: StandardKey.Cancel onActivated: inviteDialogRoot.close() @@ -39,10 +51,6 @@ ApplicationWindow { anchors.margins: 10 spacing: 10 - Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier)) - Keys.onEnterPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() - Keys.onReturnPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() - Label { text: qsTr("User ID to invite") Layout.fillWidth: true @@ -64,14 +72,8 @@ ApplicationWindow { } Component.onCompleted: forceActiveFocus() -// Shortcut { -// sequence: "Ctrl+Enter" -// onActivated: invitees.accept() -// } - Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier)) - Keys.onEnterPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() - Keys.onReturnPressed: if (event.modifiers & Qt.ControlModifier) invitees.accept() + Keys.onPressed: if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier)) cleanUpAndClose() } @@ -208,10 +210,7 @@ ApplicationWindow { Button { text: qsTr("Invite") DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole - onClicked: { - invitees.accept(); - inviteDialogRoot.close(); - } + onClicked: cleanUpAndClose() } Button { From 74d493ff1672eb9f8cf333bb5c11f80eb178eeb5 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 19 Jul 2021 10:07:54 -0400 Subject: [PATCH 33/56] Use standardized padding --- resources/qml/InviteDialog.qml | 6 +++--- resources/qml/RoomMembers.qml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 417de049..02cb5e07 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -49,7 +49,7 @@ ApplicationWindow { ColumnLayout { anchors.fill: parent anchors.margins: 10 - spacing: 10 + spacing: Nheko.paddingMedium Label { text: qsTr("User ID to invite") @@ -57,7 +57,7 @@ ApplicationWindow { } RowLayout { - spacing: 10 + spacing: Nheko.paddingMedium MatrixTextField { id: inviteeEntry @@ -165,7 +165,7 @@ ApplicationWindow { model: invitees delegate: RowLayout { - spacing: 10 + spacing: Nheko.paddingMedium Avatar { width: Nheko.avatarSize diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index da650859..f9be95a1 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -29,7 +29,7 @@ ApplicationWindow { ColumnLayout { anchors.fill: parent anchors.margins: 10 - spacing: 10 + spacing: Nheko.paddingMedium Avatar { id: roomAvatar @@ -81,7 +81,7 @@ ApplicationWindow { } delegate: RowLayout { - spacing: 10 + spacing: Nheko.paddingMedium Avatar { width: Nheko.avatarSize From 4384554587c3e9327382f2f9cbc36e893fbe4dab Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 19 Jul 2021 12:31:20 -0400 Subject: [PATCH 34/56] Only invite if there is something/someone to invite --- resources/qml/InviteDialog.qml | 81 ++-------------------------------- src/InviteesModel.cpp | 2 + src/InviteesModel.h | 3 ++ 3 files changed, 9 insertions(+), 77 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 02cb5e07..94a95861 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -18,13 +18,11 @@ ApplicationWindow { if (inviteeEntry.text.match("@.+?:.{3,}")) { invitees.addUser(inviteeEntry.text); inviteeEntry.clear(); - } else { - warningLabel.show(); } } function cleanUpAndClose() { - if (inviteeEntry.text !== "") + if (inviteeEntry.text.match("@.+?:.{3,}")) addInvite(); invitees.accept(); close(); @@ -79,80 +77,8 @@ ApplicationWindow { Button { text: qsTr("Add") - onClicked: { - if (inviteeEntry.text !== "") { - addInvite(); - } - } - } - - } - - Label { - id: warningLabel - - function show() { - state = "shown"; - warningLabelTimer.start(); - } - - text: qsTr("Please enter a valid username (e.g. @joe:matrix.org).") - color: "red" - visible: false - opacity: 0 - state: "hidden" - states: [ - State { - name: "shown" - - PropertyChanges { - target: warningLabel - opacity: 1 - visible: true - } - - }, - State { - name: "hidden" - - PropertyChanges { - target: warningLabel - opacity: 0 - visible: false - } - - } - ] - transitions: [ - Transition { - from: "shown" - to: "hidden" - reversible: true - - SequentialAnimation { - NumberAnimation { - target: warningLabel - property: "opacity" - duration: 500 - } - - PropertyAction { - target: warningLabel - property: "visible" - } - - } - - } - ] - - Timer { - id: warningLabelTimer - - interval: 2000 - repeat: false - running: false - onTriggered: warningLabel.state = "hidden" + enabled: inviteeEntry.text.match("@.+?:.{3,}") + onClicked: addInvite() } } @@ -210,6 +136,7 @@ ApplicationWindow { Button { text: qsTr("Invite") DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + enabled: invitees.count > 0 onClicked: cleanUpAndClose() } diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp index 59054690..9b64f57c 100644 --- a/src/InviteesModel.cpp +++ b/src/InviteesModel.cpp @@ -22,6 +22,8 @@ InviteesModel::addUser(QString mxid) connect(invitee, &Invitee::userInfoLoaded, this, [this]() { endInsertRows(); }); invitees_.push_back(invitee); + + emit countChanged(); } QHash diff --git a/src/InviteesModel.h b/src/InviteesModel.h index ac9208a0..a4e19ebb 100644 --- a/src/InviteesModel.h +++ b/src/InviteesModel.h @@ -30,6 +30,8 @@ class InviteesModel : public QAbstractListModel { Q_OBJECT + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) + public: enum Roles { @@ -52,6 +54,7 @@ public: signals: void accept(); + void countChanged(); private: QVector invitees_; From b6d4e6b20a5635b4acdee6e417dce0f55eaaaaa9 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 19 Jul 2021 12:31:57 -0400 Subject: [PATCH 35/56] Drop unnecessary code --- resources/qml/RoomMembers.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index f9be95a1..28e999db 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -58,7 +58,6 @@ ApplicationWindow { } ScrollView { - clip: false palette: Nheko.colors padding: 10 ScrollBar.horizontal.visible: false From e91b3067a168f9729a9d4a353ff3e2ba83a473db Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 19 Jul 2021 12:32:09 -0400 Subject: [PATCH 36/56] Fix visibility of spinner --- resources/qml/RoomMembers.qml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 28e999db..0d957165 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -114,12 +114,9 @@ ApplicationWindow { } footer: Spinner { - // This is not a wonderful solution, but it is the best way to calculate whether - // all users are loaded while keeping canFetchMore() const - - // TODO: just toggling the visiblity leaves some large empty space at the bottom - // of the list. This should be fixed. visible: members.numUsersLoaded < members.memberCount + // use the default height if it's visible, otherwise no height at all + height: visible ? undefined : 0 anchors.centerIn: parent } } From 10c6f2b43fa88830e4a693e8a71734ab90c053c2 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 19 Jul 2021 12:33:54 -0400 Subject: [PATCH 37/56] Use Nico's favored formatting (*shrugs*) --- src/MemberList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MemberList.cpp b/src/MemberList.cpp index b2ed82bd..dd5997f5 100644 --- a/src/MemberList.cpp +++ b/src/MemberList.cpp @@ -60,7 +60,7 @@ MemberList::addUsers(const std::vector &members) QHash MemberList::roleNames() const { - return {{Mxid, "mxid"}, {DisplayName, "displayName"}, {AvatarUrl, "avatarUrl"}}; + return {{Mxid, "mxid"}, {DisplayName, "displayName"}, {AvatarUrl, "avatarUrl"},}; } QVariant From 48669302ece255010f31150543d8e0ef4830fb74 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 19 Jul 2021 14:31:08 -0400 Subject: [PATCH 38/56] make lint --- resources/qml/InviteDialog.qml | 13 ++++++++----- resources/qml/RoomMembers.qml | 8 +++++++- resources/qml/Root.qml | 3 +++ src/MemberList.cpp | 6 +++++- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 94a95861..b1b1bb39 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -24,6 +24,7 @@ ApplicationWindow { function cleanUpAndClose() { if (inviteeEntry.text.match("@.+?:.{3,}")) addInvite(); + invitees.accept(); close(); } @@ -64,15 +65,17 @@ ApplicationWindow { placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.") Layout.fillWidth: true onAccepted: { - if (text !== "") { + if (text !== "") addInvite(); - } + } Component.onCompleted: forceActiveFocus() - Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier)) - Keys.onPressed: if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier)) cleanUpAndClose() - + Keys.onPressed: { + if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier)) { + cleanUpAndClose(); + } + } } Button { diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 0d957165..57353132 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -2,12 +2,12 @@ // // SPDX-License-Identifier: GPL-3.0-or-later +import "./ui" import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import QtQuick.Window 2.12 import im.nheko 1.0 -import "./ui" ApplicationWindow { id: roomMembersRoot @@ -110,7 +110,9 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true } + } + } footer: Spinner { @@ -119,12 +121,16 @@ ApplicationWindow { height: visible ? undefined : 0 anchors.centerIn: parent } + } + } + } footer: DialogButtonBox { standardButtons: DialogButtonBox.Ok onAccepted: roomMembersRoot.close() } + } diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml index 31d00585..102d0411 100644 --- a/resources/qml/Root.qml +++ b/resources/qml/Root.qml @@ -52,6 +52,7 @@ Page { RoomMembers { } + } Component { @@ -77,11 +78,13 @@ Page { } } + Component { id: inviteDialog InviteDialog { } + } Shortcut { diff --git a/src/MemberList.cpp b/src/MemberList.cpp index dd5997f5..04377a0f 100644 --- a/src/MemberList.cpp +++ b/src/MemberList.cpp @@ -60,7 +60,11 @@ MemberList::addUsers(const std::vector &members) QHash MemberList::roleNames() const { - return {{Mxid, "mxid"}, {DisplayName, "displayName"}, {AvatarUrl, "avatarUrl"},}; + return { + {Mxid, "mxid"}, + {DisplayName, "displayName"}, + {AvatarUrl, "avatarUrl"}, + }; } QVariant From a9ed83a1cef0b2ac156e784276ccd01dc2fbf7e8 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 19 Jul 2021 14:34:04 -0400 Subject: [PATCH 39/56] Remove size_t property stuff --- src/MemberList.h | 4 ++-- src/main.cpp | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/MemberList.h b/src/MemberList.h index aa240b7a..cc3b75f7 100644 --- a/src/MemberList.h +++ b/src/MemberList.h @@ -12,7 +12,7 @@ class MemberList : public QAbstractListModel Q_OBJECT Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged) - Q_PROPERTY(size_t memberCount READ memberCount NOTIFY memberCountChanged) + Q_PROPERTY(int memberCount READ memberCount NOTIFY memberCountChanged) Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged) Q_PROPERTY(QString roomId READ roomId NOTIFY roomIdChanged) Q_PROPERTY(int numUsersLoaded READ numUsersLoaded NOTIFY numUsersLoadedChanged) @@ -35,7 +35,7 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QString roomName() const { return QString::fromStdString(info_.name); } - size_t memberCount() const { return info_.member_count; } + int memberCount() const { return info_.member_count; } QString avatarUrl() const { return QString::fromStdString(info_.avatar_url); } QString roomId() const { return room_id_; } int numUsersLoaded() const { return numUsersLoaded_; } diff --git a/src/main.cpp b/src/main.cpp index 376fc4f5..29e93d49 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -205,9 +205,6 @@ main(int argc, char *argv[]) parser.process(app); - // make sure that size_t properties will work - qRegisterMetaType("size_t"); - app.setWindowIcon(QIcon::fromTheme("nheko", QIcon{":/logos/nheko.png"})); http::init(); From a7bdbc2af26bc3c6af0e1061d63a479a2bacdd2e Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 19 Jul 2021 14:37:16 -0400 Subject: [PATCH 40/56] Consolidate connections --- resources/qml/Root.qml | 6 ------ resources/qml/TimelineView.qml | 10 ---------- 2 files changed, 16 deletions(-) diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml index 102d0411..0a0f90cf 100644 --- a/resources/qml/Root.qml +++ b/resources/qml/Root.qml @@ -141,20 +141,14 @@ Page { }); membersDialog.show(); } - } - Connections { - target: Rooms.currentRoom onOpenRoomSettingsDialog: { var roomSettings = roomSettingsComponent.createObject(timelineRoot, { "roomSettings": settings }); roomSettings.show(); } - } - Connections { - target: Rooms.currentRoom onOpenInviteUsersDialog: { var dialog = inviteDialog.createObject(timelineRoot, { "roomId": Rooms.currentRoom.roomId, diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index f5979e14..c5cc69a6 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -249,14 +249,4 @@ Item { roomid: room ? room.roomId : "" } - Connections { - target: room - onOpenRoomSettingsDialog: { - var roomSettings = roomSettingsComponent.createObject(timelineRoot, { - "roomSettings": settings - }); - roomSettings.show(); - } - } - } From 21eb312f69111b8141e2ac9da5bb3871287045bd Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 19 Jul 2021 14:49:57 -0400 Subject: [PATCH 41/56] Only run spinner while loading members --- resources/qml/RoomMembers.qml | 2 +- src/MemberList.cpp | 6 ++++++ src/MemberList.h | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 57353132..b190be07 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -116,7 +116,7 @@ ApplicationWindow { } footer: Spinner { - visible: members.numUsersLoaded < members.memberCount + visible: members.numUsersLoaded < members.memberCount && members.loadingMoreMembers // use the default height if it's visible, otherwise no height at all height: visible ? undefined : 0 anchors.centerIn: parent diff --git a/src/MemberList.cpp b/src/MemberList.cpp index 04377a0f..415e3b57 100644 --- a/src/MemberList.cpp +++ b/src/MemberList.cpp @@ -98,8 +98,14 @@ MemberList::canFetchMore(const QModelIndex &) const void MemberList::fetchMore(const QModelIndex &) { + loadingMoreMembers_ = true; + emit loadingMoreMembersChanged(); + auto members = cache::getMembers(room_id_.toStdString(), rowCount()); addUsers(members); numUsersLoaded_ += members.size(); emit numUsersLoadedChanged(); + + loadingMoreMembers_ = false; + emit loadingMoreMembersChanged(); } diff --git a/src/MemberList.h b/src/MemberList.h index cc3b75f7..070666a2 100644 --- a/src/MemberList.h +++ b/src/MemberList.h @@ -16,6 +16,7 @@ class MemberList : public QAbstractListModel Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged) Q_PROPERTY(QString roomId READ roomId NOTIFY roomIdChanged) Q_PROPERTY(int numUsersLoaded READ numUsersLoaded NOTIFY numUsersLoadedChanged) + Q_PROPERTY(bool loadingMoreMembers READ loadingMoreMembers NOTIFY loadingMoreMembersChanged) public: enum Roles @@ -39,6 +40,7 @@ public: QString avatarUrl() const { return QString::fromStdString(info_.avatar_url); } QString roomId() const { return room_id_; } int numUsersLoaded() const { return numUsersLoaded_; } + bool loadingMoreMembers() const { return loadingMoreMembers_; } signals: void roomNameChanged(); @@ -46,6 +48,7 @@ signals: void avatarUrlChanged(); void roomIdChanged(); void numUsersLoadedChanged(); + void loadingMoreMembersChanged(); public slots: void addUsers(const std::vector &users); @@ -59,4 +62,5 @@ private: QString room_id_; RoomInfo info_; int numUsersLoaded_{0}; + bool loadingMoreMembers_{false}; }; From 7cd4e6f1c6fd73edcdeb3cc6918f009d100d2fa1 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 19 Jul 2021 15:05:36 -0400 Subject: [PATCH 42/56] make lint --- resources/qml/InviteDialog.qml | 4 ++-- resources/qml/Root.qml | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index b1b1bb39..1eaaef8f 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -72,9 +72,9 @@ ApplicationWindow { Component.onCompleted: forceActiveFocus() Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier)) Keys.onPressed: { - if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier)) { + if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier)) cleanUpAndClose(); - } + } } diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml index 0a0f90cf..b5395232 100644 --- a/resources/qml/Root.qml +++ b/resources/qml/Root.qml @@ -141,14 +141,12 @@ Page { }); membersDialog.show(); } - onOpenRoomSettingsDialog: { var roomSettings = roomSettingsComponent.createObject(timelineRoot, { "roomSettings": settings }); roomSettings.show(); } - onOpenInviteUsersDialog: { var dialog = inviteDialog.createObject(timelineRoot, { "roomId": Rooms.currentRoom.roomId, From 6c9ac76260ebdd078c1c4b8d2fdc1eb7caef73c4 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 19 Jul 2021 17:41:47 -0400 Subject: [PATCH 43/56] Fix roomId property --- resources/qml/MessageInput.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml index 415d67a7..c135aff9 100644 --- a/resources/qml/MessageInput.qml +++ b/resources/qml/MessageInput.qml @@ -331,7 +331,7 @@ Rectangle { image: ":/icons/icons/ui/sticky-note-solid.svg" ToolTip.visible: hovered ToolTip.text: qsTr("Stickers") - onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId(), function(row) { + onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId, function(row) { room.input.sticker(stickerPopup.model.sourceModel, row); TimelineManager.focusMessageInput(); }) From 152acdc4a5eb3604515d1b6b7645446c332de1eb Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 10:10:43 -0400 Subject: [PATCH 44/56] Fix hardcoded spacing/padding Another padding fix --- resources/qml/InviteDialog.qml | 4 ++-- resources/qml/RoomMembers.qml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 1eaaef8f..e80087fc 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -47,7 +47,7 @@ ApplicationWindow { ColumnLayout { anchors.fill: parent - anchors.margins: 10 + anchors.margins: Nheko.paddingMedium spacing: Nheko.paddingMedium Label { @@ -106,7 +106,7 @@ ApplicationWindow { } ColumnLayout { - spacing: 5 + spacing: Nheko.paddingSmall Label { text: model.displayName diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index b190be07..11bd486c 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -28,7 +28,7 @@ ApplicationWindow { ColumnLayout { anchors.fill: parent - anchors.margins: 10 + anchors.margins: Nheko.paddingMedium spacing: Nheko.paddingMedium Avatar { @@ -59,7 +59,7 @@ ApplicationWindow { ScrollView { palette: Nheko.colors - padding: 10 + padding: Nheko.paddingMedium ScrollBar.horizontal.visible: false Layout.fillHeight: true Layout.minimumHeight: 200 @@ -69,7 +69,7 @@ ApplicationWindow { id: memberList clip: true - spacing: 8 + spacing: Nheko.paddingMedium boundsBehavior: Flickable.StopAtBounds model: members @@ -92,7 +92,7 @@ ApplicationWindow { } ColumnLayout { - spacing: 5 + spacing: Nheko.paddingSmall Label { text: model.displayName From fa06881c49a48c69223ceac7ad3d06105db9ed50 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 12:53:16 -0400 Subject: [PATCH 45/56] Don't hardcode fonts Fix hardcoded fonts (again) --- resources/qml/InviteDialog.qml | 4 ++-- resources/qml/RoomMembers.qml | 6 +++--- resources/qml/RoomSettings.qml | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index e80087fc..d0e6a645 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -111,13 +111,13 @@ ApplicationWindow { Label { text: model.displayName color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window) - font.pointSize: 12 + font.pointSize: fontMetrics.font.pointSize } Label { text: model.mxid color: Nheko.colors.buttonText - font.pointSize: 10 + font.pointSize: fontMetrics.font.pointSize * 0.9 } Item { diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 11bd486c..b9b800c5 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -43,7 +43,7 @@ ApplicationWindow { } Label { - font.pixelSize: 24 + font.pixelSize: fontMetrics.font.pixelSize * 2 text: members.memberCount + (members.memberCount === 1 ? qsTr(" person in ") : qsTr(" people in ")) + members.roomName Layout.alignment: Qt.AlignHCenter } @@ -97,13 +97,13 @@ ApplicationWindow { Label { text: model.displayName color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window) - font.pointSize: 12 + font.pointSize: fontMetrics.font.pointSize } Label { text: model.mxid color: Nheko.colors.buttonText - font.pointSize: 10 + font.pointSize: fontMetrics.font.pointSize * 0.9 } Item { diff --git a/resources/qml/RoomSettings.qml b/resources/qml/RoomSettings.qml index a27be13e..2701edf9 100644 --- a/resources/qml/RoomSettings.qml +++ b/resources/qml/RoomSettings.qml @@ -98,7 +98,7 @@ ApplicationWindow { MatrixText { text: roomSettings.roomName - font.pixelSize: 24 + font.pixelSize: fontMetrics.font.pixelSize * 2 Layout.alignment: Qt.AlignHCenter } @@ -264,7 +264,7 @@ ApplicationWindow { MatrixText { text: roomSettings.roomId - font.pixelSize: 14 + font.pixelSize: fontMetrics.font.pixelSize * 1.2 Layout.alignment: Qt.AlignRight } @@ -274,7 +274,7 @@ ApplicationWindow { MatrixText { text: roomSettings.roomVersion - font.pixelSize: 14 + font.pixelSize: fontMetrics.font.pixelSize * 1.2 Layout.alignment: Qt.AlignRight } From 75920925dc455157fd4a58f3a1e53b1fc6b86ad7 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 18:04:19 -0400 Subject: [PATCH 46/56] Use correct colors --- resources/qml/InviteDialog.qml | 2 ++ resources/qml/RoomMembers.qml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index d0e6a645..1be2e06c 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -34,6 +34,8 @@ ApplicationWindow { y: MainWindow.y + (MainWindow.height / 2) - (height / 2) height: 380 width: 340 + palette: Nheko.colors + color: Nheko.colors.window Shortcut { sequence: "Ctrl+Enter" diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index b9b800c5..1cf41e13 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -20,6 +20,8 @@ ApplicationWindow { height: 650 width: 420 minimumHeight: 420 + palette: Nheko.colors + color: Nheko.colors.window Shortcut { sequence: StandardKey.Cancel From 92fdda84236153b31f3c0dcd393c9db146b97b96 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 18:27:16 -0400 Subject: [PATCH 47/56] Use elided label --- resources/qml/RoomMembers.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 1cf41e13..09c7391e 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -44,9 +44,9 @@ ApplicationWindow { onClicked: Rooms.currentRoom.openRoomSettings(members.roomId) } - Label { + ElidedLabel { font.pixelSize: fontMetrics.font.pixelSize * 2 - text: members.memberCount + (members.memberCount === 1 ? qsTr(" person in ") : qsTr(" people in ")) + members.roomName + fullText: members.memberCount + (members.memberCount === 1 ? qsTr(" person in ") : qsTr(" people in ")) + members.roomName Layout.alignment: Qt.AlignHCenter } From 823e74039698daac8bd45bf13c71b193c60ea226 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 18:27:29 -0400 Subject: [PATCH 48/56] Check with regex everywhere --- resources/qml/InviteDialog.qml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 1be2e06c..026e3297 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -15,14 +15,14 @@ ApplicationWindow { property InviteesModel invitees function addInvite() { - if (inviteeEntry.text.match("@.+?:.{3,}")) { + if (inviteeEntry.isValidMxid) { invitees.addUser(inviteeEntry.text); inviteeEntry.clear(); } } function cleanUpAndClose() { - if (inviteeEntry.text.match("@.+?:.{3,}")) + if (inviteeEntry.isValidMxid) addInvite(); invitees.accept(); @@ -63,11 +63,13 @@ ApplicationWindow { MatrixTextField { id: inviteeEntry + property bool isValidMxid: text.match("@.+?:.{3,}") + backgroundColor: Nheko.colors.window placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.") Layout.fillWidth: true onAccepted: { - if (text !== "") + if (isValidMxid) addInvite(); } @@ -82,7 +84,7 @@ ApplicationWindow { Button { text: qsTr("Add") - enabled: inviteeEntry.text.match("@.+?:.{3,}") + enabled: inviteeEntry.isValidMxid onClicked: addInvite() } From 44d2818e0cb2bceb25e29edf7ef32d02ede42432 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 19:17:20 -0400 Subject: [PATCH 49/56] Add property for plain room name --- resources/qml/InviteDialog.qml | 4 ++-- resources/qml/Root.qml | 2 +- src/timeline/TimelineModel.cpp | 3 +++ src/timeline/TimelineModel.h | 2 ++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index 026e3297..e9ff475d 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -11,7 +11,7 @@ ApplicationWindow { id: inviteDialogRoot property string roomId - property string roomName + property string plainRoomName property InviteesModel invitees function addInvite() { @@ -29,7 +29,7 @@ ApplicationWindow { close(); } - title: qsTr("Invite users to ") + roomName + title: qsTr("Invite users to ") + plainRoomName x: MainWindow.x + (MainWindow.width / 2) - (width / 2) y: MainWindow.y + (MainWindow.height / 2) - (height / 2) height: 380 diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml index b5395232..f71c18e2 100644 --- a/resources/qml/Root.qml +++ b/resources/qml/Root.qml @@ -150,7 +150,7 @@ Page { onOpenInviteUsersDialog: { var dialog = inviteDialog.createObject(timelineRoot, { "roomId": Rooms.currentRoom.roomId, - "roomName": Rooms.currentRoom.roomName, + "plainRoomName": Rooms.currentRoom.plainRoomName, "invitees": invitees }); dialog.show(); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 7ce0e98a..e431e1ac 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -327,6 +327,9 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj this->isSpace_ = create->content.type == mtx::events::state::room_type::space; this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString()); + // this connection will simplify adding the plainRoomNameChanged() signal everywhere that it needs to be + connect(this, &TimelineModel::roomNameChanged, this, &TimelineModel::plainRoomNameChanged); + connect( this, &TimelineModel::redactionFailed, diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index ebf24bec..0d1eb1f9 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -162,6 +162,7 @@ class TimelineModel : public QAbstractListModel bool paginationInProgress READ paginationInProgress NOTIFY paginationInProgressChanged) Q_PROPERTY(QString roomId READ roomId CONSTANT) Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged) + Q_PROPERTY(QString plainRoomName READ plainRoomName NOTIFY plainRoomNameChanged) Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY roomAvatarUrlChanged) Q_PROPERTY(QString roomTopic READ roomTopic NOTIFY roomTopicChanged) Q_PROPERTY(int roomMemberCount READ roomMemberCount NOTIFY roomMemberCountChanged) @@ -367,6 +368,7 @@ signals: void encryptionChanged(); void roomNameChanged(); + void plainRoomNameChanged(); void roomTopicChanged(); void roomAvatarUrlChanged(); void roomMemberCountChanged(); From 705c283dcb3b5eb1002c699a53f609d1b5c71a9e Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 19:17:31 -0400 Subject: [PATCH 50/56] Fix bad connection --- resources/qml/InviteDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml index e9ff475d..dbe8bb07 100644 --- a/resources/qml/InviteDialog.qml +++ b/resources/qml/InviteDialog.qml @@ -106,7 +106,7 @@ ApplicationWindow { userid: model.mxid url: model.avatarUrl.replace("mxc://", "image://MxcImage/") displayName: model.displayName - onClicked: TimelineManager.timeline.openUserProfile(model.mxid) + onClicked: Rooms.currentRoom.openUserProfile(model.mxid) } ColumnLayout { From c78c2848988cd7d0c0bb51209359f6b925d2da34 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 19:17:49 -0400 Subject: [PATCH 51/56] Call the correct function --- resources/qml/RoomMembers.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 09c7391e..8431dc99 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -56,7 +56,7 @@ ApplicationWindow { hoverEnabled: true ToolTip.visible: hovered ToolTip.text: qsTr("Invite more people") - onClicked: Rooms.currentRoom.openInviteUsersDialog() + onClicked: Rooms.currentRoom.openInviteUsers() } ScrollView { From 77c636f3d3f0bc11c2fc5e6cc90ee58bd42ab5f9 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 19:20:55 -0400 Subject: [PATCH 52/56] Insert user before loading avatar/display name --- src/InviteesModel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp index 9b64f57c..7bc2b2d4 100644 --- a/src/InviteesModel.cpp +++ b/src/InviteesModel.cpp @@ -19,10 +19,12 @@ InviteesModel::addUser(QString mxid) beginInsertRows(QModelIndex(), invitees_.count(), invitees_.count()); auto invitee = new Invitee{mxid, this}; - connect(invitee, &Invitee::userInfoLoaded, this, [this]() { endInsertRows(); }); + auto indexOfInvitee = invitees_.count(); + connect(invitee, &Invitee::userInfoLoaded, this, [this, indexOfInvitee]() { emit dataChanged(index(indexOfInvitee), index(indexOfInvitee)); }); invitees_.push_back(invitee); + endInsertRows(); emit countChanged(); } From efda94ca50c3942d0eb3165ce30490edd4823462 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 19:21:04 -0400 Subject: [PATCH 53/56] Modify message to be more accurate --- src/InviteesModel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp index 7bc2b2d4..f73fddd9 100644 --- a/src/InviteesModel.cpp +++ b/src/InviteesModel.cpp @@ -69,7 +69,8 @@ Invitee::Invitee(const QString &mxid, QObject *parent) mxid_.toStdString(), [this](const mtx::responses::Profile &res, mtx::http::RequestErr err) { if (err) { - nhlog::net()->warn("failed to retrieve own profile info"); + nhlog::net()->warn("failed to retrieve profile info"); + emit userInfoLoaded(); return; } From 6458614ea11d6ce25936e7ca1ac550eedfa9942a Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 19:41:12 -0400 Subject: [PATCH 54/56] make lint --- src/InviteesModel.cpp | 6 ++++-- src/timeline/TimelineModel.cpp | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp index f73fddd9..27b2116f 100644 --- a/src/InviteesModel.cpp +++ b/src/InviteesModel.cpp @@ -18,9 +18,11 @@ InviteesModel::addUser(QString mxid) { beginInsertRows(QModelIndex(), invitees_.count(), invitees_.count()); - auto invitee = new Invitee{mxid, this}; + auto invitee = new Invitee{mxid, this}; auto indexOfInvitee = invitees_.count(); - connect(invitee, &Invitee::userInfoLoaded, this, [this, indexOfInvitee]() { emit dataChanged(index(indexOfInvitee), index(indexOfInvitee)); }); + connect(invitee, &Invitee::userInfoLoaded, this, [this, indexOfInvitee]() { + emit dataChanged(index(indexOfInvitee), index(indexOfInvitee)); + }); invitees_.push_back(invitee); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index e431e1ac..66d931fd 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -327,7 +327,8 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj this->isSpace_ = create->content.type == mtx::events::state::room_type::space; this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString()); - // this connection will simplify adding the plainRoomNameChanged() signal everywhere that it needs to be + // this connection will simplify adding the plainRoomNameChanged() signal everywhere that it + // needs to be connect(this, &TimelineModel::roomNameChanged, this, &TimelineModel::plainRoomNameChanged); connect( From 38c6aa65fa3e4230a4e1de9cc8e7e2204d3896ed Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 19:45:28 -0400 Subject: [PATCH 55/56] Fix elided width --- resources/qml/RoomMembers.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 8431dc99..6f847ccc 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -48,6 +48,7 @@ ApplicationWindow { font.pixelSize: fontMetrics.font.pixelSize * 2 fullText: members.memberCount + (members.memberCount === 1 ? qsTr(" person in ") : qsTr(" people in ")) + members.roomName Layout.alignment: Qt.AlignHCenter + elideWidth: parent.width - Nheko.paddingMedium } ImageButton { From d33538316c81b548e9b6724afc3486c17ad925f2 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 20 Jul 2021 19:57:36 -0400 Subject: [PATCH 56/56] Fix the loading spinner setup This fixes binding loops and gives it a (in my opinion) sane size. --- resources/qml/RoomMembers.qml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 6f847ccc..3758cb0b 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -118,11 +118,21 @@ ApplicationWindow { } - footer: Spinner { - visible: members.numUsersLoaded < members.memberCount && members.loadingMoreMembers + footer: Item { + width: parent.width + visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers + // use the default height if it's visible, otherwise no height at all - height: visible ? undefined : 0 - anchors.centerIn: parent + height: membersLoadingSpinner.height + anchors.margins: Nheko.paddingMedium + + Spinner { + id: membersLoadingSpinner + + anchors.centerIn: parent + height: visible ? 35 : 0 + } + } }