summary refs log tree commit diff
diff options
context:
space:
mode:
authorKonstantinos Sideris <sideris.konstantin@gmail.com>2017-12-10 23:59:50 +0200
committerKonstantinos Sideris <sideris.konstantin@gmail.com>2017-12-10 23:59:50 +0200
commitef0b0f68795786751b04615451d42dbd7b3d7a5d (patch)
treea04f4677032d054d803e0a67929fbfe4d46eb87a
parentAdd gui option for joining rooms (#25) (diff)
downloadnheko-ef0b0f68795786751b04615451d42dbd7b3d7a5d.tar.xz
Add menu to invite users
-rw-r--r--CMakeLists.txt4
-rw-r--r--include/InviteeItem.h27
-rw-r--r--include/MatrixClient.h2
-rw-r--r--include/TopRoomBar.h6
-rw-r--r--include/dialogs/InviteUsers.h41
-rw-r--r--resources/icons/ui/remove-symbol.pngbin0 -> 533 bytes
-rw-r--r--resources/icons/ui/remove-symbol@2x.pngbin0 -> 699 bytes
-rw-r--r--resources/res.qrc2
-rw-r--r--resources/styles/nheko-dark.qss6
-rw-r--r--resources/styles/nheko.qss6
-rw-r--r--resources/styles/system.qss5
-rw-r--r--src/ChatPage.cc10
-rw-r--r--src/InviteeItem.cc37
-rw-r--r--src/MatrixClient.cc30
-rw-r--r--src/TopRoomBar.cc35
-rw-r--r--src/dialogs/InviteUsers.cc149
-rw-r--r--src/ui/SnackBar.cc4
17 files changed, 360 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1591c36f..b6affe75 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -142,6 +142,7 @@ endif()
 set(SRC_FILES
     # Dialogs
     src/dialogs/ImageOverlay.cc
+    src/dialogs/InviteUsers.cc
     src/dialogs/JoinRoom.cc
     src/dialogs/LeaveRoom.cc
     src/dialogs/Logout.cc
@@ -185,6 +186,7 @@ set(SRC_FILES
     src/Cache.cc
     src/ChatPage.cc
     src/Deserializable.cc
+    src/InviteeItem.cc
     src/InputValidator.cc
     src/Login.cc
     src/LoginPage.cc
@@ -218,6 +220,7 @@ include_directories(${LMDB_INCLUDE_DIR})
 qt5_wrap_cpp(MOC_HEADERS
     # Dialogs
     include/dialogs/ImageOverlay.h
+    include/dialogs/InviteUsers.h
     include/dialogs/JoinRoom.h
     include/dialogs/LeaveRoom.h
     include/dialogs/Logout.h
@@ -259,6 +262,7 @@ qt5_wrap_cpp(MOC_HEADERS
     include/ChatPage.h
     include/LoginPage.h
     include/MainWindow.h
+    include/InviteeItem.h
     include/MatrixClient.h
     include/QuickSwitcher.h
     include/RegisterPage.h
diff --git a/include/InviteeItem.h b/include/InviteeItem.h
new file mode 100644
index 00000000..f0bdbdf0
--- /dev/null
+++ b/include/InviteeItem.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <QLabel>
+#include <QWidget>
+
+#include "mtx.hpp"
+
+class FlatButton;
+
+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_;
+        FlatButton *removeUserBtn_;
+};
diff --git a/include/MatrixClient.h b/include/MatrixClient.h
index 397ba11d..ee493da6 100644
--- a/include/MatrixClient.h
+++ b/include/MatrixClient.h
@@ -60,6 +60,7 @@ public:
         void sendTypingNotification(const QString &roomid, int timeoutInMillis = 20000);
         void removeTypingNotification(const QString &roomid);
         void readEvent(const QString &room_id, const QString &event_id);
+        void inviteUser(const QString &room_id, const QString &user);
 
         QUrl getHomeServer() { return server_; };
         int transactionId() { return txn_id_; };
@@ -84,6 +85,7 @@ signals:
         void versionError(const QString &error);
 
         void loggedOut();
+        void invitedUser(const QString &room_id, const QString &user);
 
         void loginSuccess(const QString &userid, const QString &homeserver, const QString &token);
         void registerSuccess(const QString &userid,
diff --git a/include/TopRoomBar.h b/include/TopRoomBar.h
index 7bd10356..471662a4 100644
--- a/include/TopRoomBar.h
+++ b/include/TopRoomBar.h
@@ -26,6 +26,7 @@
 #include <QSharedPointer>
 #include <QVBoxLayout>
 
+#include "dialogs/InviteUsers.h"
 #include "dialogs/LeaveRoom.h"
 
 class Avatar;
@@ -56,6 +57,7 @@ public:
 
 signals:
         void leaveRoom();
+        void inviteUsers(QStringList users);
 
 protected:
         void paintEvent(QPaintEvent *event) override;
@@ -76,12 +78,16 @@ private:
         QMenu *menu_;
         QAction *toggleNotifications_;
         QAction *leaveRoom_;
+        QAction *inviteUsers_;
 
         FlatButton *settingsBtn_;
 
         QSharedPointer<OverlayModal> leaveRoomModal_;
         QSharedPointer<dialogs::LeaveRoom> leaveRoomDialog_;
 
+        QSharedPointer<OverlayModal> inviteUsersModal_;
+        QSharedPointer<dialogs::InviteUsers> inviteUsersDialog_;
+
         Avatar *avatar_;
 
         int buttonSize_;
diff --git a/include/dialogs/InviteUsers.h b/include/dialogs/InviteUsers.h
new file mode 100644
index 00000000..236a2558
--- /dev/null
+++ b/include/dialogs/InviteUsers.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <QFrame>
+#include <QLabel>
+#include <QListWidgetItem>
+#include <QStringList>
+
+class FlatButton;
+class TextField;
+class QListWidget;
+
+namespace dialogs {
+
+class InviteUsers : public QFrame
+{
+        Q_OBJECT
+public:
+        explicit InviteUsers(QWidget *parent = nullptr);
+
+protected:
+        void paintEvent(QPaintEvent *event) override;
+
+signals:
+        void closing(bool isLeaving, QStringList invitees);
+
+private slots:
+        void removeInvitee(QListWidgetItem *item);
+
+private:
+        void addUser();
+        QStringList invitedUsers() const;
+
+        FlatButton *confirmBtn_;
+        FlatButton *cancelBtn_;
+
+        TextField *inviteeInput_;
+        QLabel *errorLabel_;
+
+        QListWidget *inviteeList_;
+};
+} // dialogs
diff --git a/resources/icons/ui/remove-symbol.png b/resources/icons/ui/remove-symbol.png
new file mode 100644
index 00000000..0b610853
--- /dev/null
+++ b/resources/icons/ui/remove-symbol.png
Binary files differdiff --git a/resources/icons/ui/remove-symbol@2x.png b/resources/icons/ui/remove-symbol@2x.png
new file mode 100644
index 00000000..aa37086b
--- /dev/null
+++ b/resources/icons/ui/remove-symbol@2x.png
Binary files differdiff --git a/resources/res.qrc b/resources/res.qrc
index d15dd04c..83415e9b 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -30,6 +30,8 @@
         <file>icons/ui/play-sign@2x.png</file>
         <file>icons/ui/pause-symbol.png</file>
         <file>icons/ui/pause-symbol@2x.png</file>
+        <file>icons/ui/remove-symbol.png</file>
+        <file>icons/ui/remove-symbol@2x.png</file>
 
         <file>icons/emoji-categories/people.png</file>
         <file>icons/emoji-categories/people@2x.png</file>
diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss
index 5704fee1..42ee6740 100644
--- a/resources/styles/nheko-dark.qss
+++ b/resources/styles/nheko-dark.qss
@@ -79,11 +79,17 @@ Avatar {
 
 dialogs--Logout,
 dialogs--LeaveRoom,
+dialogs--InviteUsers,
 dialogs--JoinRoom {
     background-color: #383c4a;
     color: #caccd1;
 }
 
+QListWidget {
+    background-color: #383c4a;
+    color: #caccd1;
+}
+
 WelcomePage,
 LoginPage,
 RegisterPage {
diff --git a/resources/styles/nheko.qss b/resources/styles/nheko.qss
index 8ffe625b..6c592ac8 100644
--- a/resources/styles/nheko.qss
+++ b/resources/styles/nheko.qss
@@ -81,11 +81,17 @@ Avatar {
 
 dialogs--Logout,
 dialogs--LeaveRoom,
+dialogs--InviteUsers,
 dialogs--JoinRoom {
     background-color: white;
     color: #333;
 }
 
+QListWidget {
+    background-color: white;
+    color: #333;
+}
+
 WelcomePage,
 LoginPage,
 RegisterPage {
diff --git a/resources/styles/system.qss b/resources/styles/system.qss
index cc54402f..f3bf619d 100644
--- a/resources/styles/system.qss
+++ b/resources/styles/system.qss
@@ -89,3 +89,8 @@ ScrollBar {
     qproperty-handleColor: palette(text);
     qproperty-backgroundColor: palette(window);
 }
+
+QListWidget {
+    background-color: palette(window);
+    color: palette(text);
+}
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index 25b8fe66..dfae487d 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -109,6 +109,13 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
 
         connect(
           top_bar_, &TopRoomBar::leaveRoom, this, [=]() { client_->leaveRoom(current_room_); });
+        connect(top_bar_, &TopRoomBar::inviteUsers, this, [=](QStringList users) {
+                for (int ii = 0; ii < users.size(); ++ii) {
+                        QTimer::singleShot(ii * 1000, this, [=]() {
+                                client_->inviteUser(current_room_, users.at(ii));
+                        });
+                }
+        });
 
         connect(room_list_, &RoomList::roomChanged, this, [=](const QString &roomid) {
                 QStringList users;
@@ -258,6 +265,9 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
         connect(client_.data(), &MatrixClient::joinedRoom, this, [=]() {
                 emit showNotification("You joined the room.");
         });
+        connect(client_.data(), &MatrixClient::invitedUser, this, [=](QString, QString user) {
+                emit showNotification(QString("Invited user %1").arg(user));
+        });
         connect(client_.data(), &MatrixClient::leftRoom, this, &ChatPage::removeRoom);
 
         showContentTimer_ = new QTimer(this);
diff --git a/src/InviteeItem.cc b/src/InviteeItem.cc
new file mode 100644
index 00000000..c544032c
--- /dev/null
+++ b/src/InviteeItem.cc
@@ -0,0 +1,37 @@
+#include <QHBoxLayout>
+
+#include "FlatButton.h"
+#include "InviteeItem.h"
+#include "Theme.h"
+
+constexpr int SidePadding = 10;
+constexpr int IconSize    = 13;
+
+InviteeItem::InviteeItem(mtx::identifiers::User user, QWidget *parent)
+  : QWidget{parent}
+  , user_{QString::fromStdString(user.toString())}
+{
+        auto topLayout_ = new QHBoxLayout(this);
+        topLayout_->setSpacing(0);
+        topLayout_->setContentsMargins(SidePadding, 0, 3 * SidePadding, 0);
+
+        QFont font;
+        font.setPixelSize(15);
+
+        name_ = new QLabel(user_, this);
+        name_->setFont(font);
+
+        QIcon removeUserIcon;
+        removeUserIcon.addFile(":/icons/icons/ui/remove-symbol.png");
+
+        removeUserBtn_ = new FlatButton(this);
+        removeUserBtn_->setIcon(removeUserIcon);
+        removeUserBtn_->setIconSize(QSize(IconSize, IconSize));
+        removeUserBtn_->setFixedSize(QSize(IconSize, IconSize));
+        removeUserBtn_->setRippleStyle(ui::RippleStyle::NoRipple);
+
+        topLayout_->addWidget(name_);
+        topLayout_->addWidget(removeUserBtn_);
+
+        connect(removeUserBtn_, &FlatButton::clicked, this, &InviteeItem::removeItem);
+}
diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index 2878c4bb..b18b6e4a 100644
--- a/src/MatrixClient.cc
+++ b/src/MatrixClient.cc
@@ -861,6 +861,36 @@ MatrixClient::leaveRoom(const QString &roomId)
 }
 
 void
+MatrixClient::inviteUser(const QString &roomId, const QString &user)
+{
+        QUrlQuery query;
+        query.addQueryItem("access_token", token_);
+
+        QUrl endpoint(server_);
+        endpoint.setPath(clientApiUrl_ + QString("/rooms/%1/invite").arg(roomId));
+        endpoint.setQuery(query);
+
+        QNetworkRequest request(endpoint);
+        request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
+
+        QJsonObject body{{"user_id", user}};
+        auto reply = post(request, QJsonDocument(body).toJson(QJsonDocument::Compact));
+
+        connect(reply, &QNetworkReply::finished, this, [this, reply, roomId, user]() {
+                reply->deleteLater();
+
+                int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+                if (status == 0 || status >= 400) {
+                        // TODO: Handle failure.
+                        qWarning() << reply->errorString();
+                        return;
+                }
+
+                emit invitedUser(roomId, user);
+        });
+}
+void
 MatrixClient::sendTypingNotification(const QString &roomid, int timeoutInMillis)
 {
         QSettings settings;
diff --git a/src/TopRoomBar.cc b/src/TopRoomBar.cc
index 51a3af68..9ad30064 100644
--- a/src/TopRoomBar.cc
+++ b/src/TopRoomBar.cc
@@ -15,6 +15,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <QDebug>
 #include <QStyleOption>
 
 #include "Avatar.h"
@@ -88,6 +89,33 @@ TopRoomBar::TopRoomBar(QWidget *parent)
                 roomSettings_->toggleNotifications();
         });
 
+        inviteUsers_ = new QAction(tr("Invite users"), this);
+        connect(inviteUsers_, &QAction::triggered, this, [=]() {
+                if (inviteUsersDialog_.isNull()) {
+                        inviteUsersDialog_ =
+                          QSharedPointer<dialogs::InviteUsers>(new dialogs::InviteUsers(this));
+
+                        connect(inviteUsersDialog_.data(),
+                                &dialogs::InviteUsers::closing,
+                                this,
+                                [=](bool isSending, QStringList invitees) {
+                                        inviteUsersModal_->fadeOut();
+
+                                        if (isSending && !invitees.isEmpty())
+                                                emit inviteUsers(invitees);
+                                });
+                }
+
+                if (inviteUsersModal_.isNull()) {
+                        inviteUsersModal_ = QSharedPointer<OverlayModal>(
+                          new OverlayModal(MainWindow::instance(), inviteUsersDialog_.data()));
+                        inviteUsersModal_->setDuration(0);
+                        inviteUsersModal_->setColor(QColor(30, 30, 30, 170));
+                }
+
+                inviteUsersModal_->fadeIn();
+        });
+
         leaveRoom_ = new QAction(tr("Leave room"), this);
         connect(leaveRoom_, &QAction::triggered, this, [=]() {
                 if (leaveRoomDialog_.isNull()) {
@@ -111,6 +139,7 @@ TopRoomBar::TopRoomBar(QWidget *parent)
         });
 
         menu_->addAction(toggleNotifications_);
+        menu_->addAction(inviteUsers_);
         menu_->addAction(leaveRoom_);
 
         connect(settingsBtn_, &QPushButton::clicked, this, [=]() {
@@ -171,9 +200,9 @@ TopRoomBar::paintEvent(QPaintEvent *event)
         QPainter painter(this);
         style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);
 
-        // Number of pixels that we can move sidebar splitter per frame. If label contains text
-        // which fills entire it's width then label starts blocking it's layout from shrinking.
-        // Making label little bit shorter leaves some space for it to shrink.
+        // Number of pixels that we can move sidebar splitter per frame. If label contains
+        // text which fills entire it's width then label starts blocking it's layout from
+        // shrinking. Making label little bit shorter leaves some space for it to shrink.
         const auto perFrameResize = 20;
 
         QString elidedText =
diff --git a/src/dialogs/InviteUsers.cc b/src/dialogs/InviteUsers.cc
new file mode 100644
index 00000000..22042453
--- /dev/null
+++ b/src/dialogs/InviteUsers.cc
@@ -0,0 +1,149 @@
+#include <QDebug>
+#include <QIcon>
+#include <QListWidget>
+#include <QListWidgetItem>
+#include <QStyleOption>
+#include <QTimer>
+#include <QVBoxLayout>
+
+#include "Config.h"
+#include "FlatButton.h"
+#include "TextField.h"
+
+#include "InviteeItem.h"
+#include "dialogs/InviteUsers.h"
+
+#include "mtx.hpp"
+
+using namespace dialogs;
+
+InviteUsers::InviteUsers(QWidget *parent)
+  : QFrame(parent)
+{
+        setMaximumSize(400, 350);
+
+        auto layout = new QVBoxLayout(this);
+        layout->setSpacing(30);
+        layout->setMargin(20);
+
+        auto buttonLayout = new QHBoxLayout();
+        buttonLayout->setSpacing(0);
+        buttonLayout->setMargin(0);
+
+        confirmBtn_ = new FlatButton("INVITE", this);
+        confirmBtn_->setFontSize(conf::btn::fontSize);
+
+        cancelBtn_ = new FlatButton(tr("CANCEL"), this);
+        cancelBtn_->setFontSize(conf::btn::fontSize);
+
+        buttonLayout->addStretch(1);
+        buttonLayout->addWidget(confirmBtn_);
+        buttonLayout->addWidget(cancelBtn_);
+
+        QFont font;
+        font.setPixelSize(conf::headerFontSize);
+
+        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);
+        font.setPixelSize(12);
+        errorLabel_->setFont(font);
+
+        layout->addWidget(inviteeInput_);
+        layout->addWidget(errorLabel_);
+        layout->addWidget(inviteeList_);
+        layout->addLayout(buttonLayout);
+
+        connect(inviteeInput_, &TextField::returnPressed, this, &InviteUsers::addUser);
+        connect(confirmBtn_, &QPushButton::clicked, [=]() {
+                emit closing(true, invitedUsers());
+
+                inviteeInput_->clear();
+                inviteeList_->clear();
+                errorLabel_->hide();
+        });
+
+        connect(cancelBtn_, &QPushButton::clicked, [=]() {
+                QStringList emptyList;
+                emit closing(false, emptyList);
+
+                inviteeInput_->clear();
+                inviteeList_->clear();
+                errorLabel_->hide();
+        });
+}
+
+void
+InviteUsers::addUser()
+{
+        auto user_id = inviteeInput_->text();
+
+        try {
+                namespace ids = mtx::identifiers;
+                auto user     = ids::parse<ids::User>(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);
+}
+
+void
+InviteUsers::paintEvent(QPaintEvent *)
+{
+        QStyleOption opt;
+        opt.init(this);
+        QPainter p(this);
+        style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
+}
+
+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<InviteeItem *>(widget);
+
+                if (invitee)
+                        users << invitee->userID();
+                else
+                        qDebug() << "Cast InviteeItem failed";
+        }
+
+        return users;
+}
diff --git a/src/ui/SnackBar.cc b/src/ui/SnackBar.cc
index 39566e99..fb415fcd 100644
--- a/src/ui/SnackBar.cc
+++ b/src/ui/SnackBar.cc
@@ -18,7 +18,9 @@ SnackBar::SnackBar(QWidget *parent)
         offset_     = STARTING_OFFSET;
         position_   = SnackBarPosition::Top;
 
-        QFont font("Open Sans", 14, QFont::Medium);
+        QFont font("Open Sans");
+        font.setPixelSize(14);
+        font.setWeight(50);
         setFont(font);
 
         showTimer_ = new QTimer();