From 07ac7b7e85b504953b0a751a3f56b60ce0a6fb37 Mon Sep 17 00:00:00 2001
From: trilene <trilene@runbox.com>
Date: Fri, 18 Dec 2020 12:49:24 -0500
Subject: [PATCH] Port PlaceCall dialog to Qml

---
 CMakeLists.txt                   |   2 -
 resources/qml/MessageInput.qml   |  23 +++++-
 resources/qml/voip/PlaceCall.qml | 107 +++++++++++++++++++++++++
 resources/res.qrc                |   1 +
 src/CallManager.h                |   2 +-
 src/ChatPage.cpp                 |   1 -
 src/dialogs/PlaceCall.cpp        | 131 -------------------------------
 src/dialogs/PlaceCall.h          |  44 -----------
 src/timeline/InputBar.cpp        |  46 -----------
 src/timeline/InputBar.h          |   1 -
 10 files changed, 131 insertions(+), 227 deletions(-)
 create mode 100644 resources/qml/voip/PlaceCall.qml
 delete mode 100644 src/dialogs/PlaceCall.cpp
 delete mode 100644 src/dialogs/PlaceCall.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c674053fb..2365ac095 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -253,7 +253,6 @@ set(SRC_FILES
 	src/dialogs/LeaveRoom.cpp
 	src/dialogs/Logout.cpp
 	src/dialogs/MemberList.cpp
-	src/dialogs/PlaceCall.cpp
 	src/dialogs/PreviewUploadOverlay.cpp
 	src/dialogs/ReCaptcha.cpp
 	src/dialogs/ReadReceipts.cpp
@@ -471,7 +470,6 @@ qt5_wrap_cpp(MOC_HEADERS
 	src/dialogs/LeaveRoom.h
 	src/dialogs/Logout.h
 	src/dialogs/MemberList.h
-	src/dialogs/PlaceCall.h
 	src/dialogs/PreviewUploadOverlay.h
 	src/dialogs/RawMessage.h
 	src/dialogs/ReCaptcha.h
diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml
index 2847d51d6..ecacedba2 100644
--- a/resources/qml/MessageInput.qml
+++ b/resources/qml/MessageInput.qml
@@ -3,6 +3,7 @@ import QtQuick.Controls 2.3
 import QtQuick.Layouts 1.2
 import QtQuick.Window 2.2
 import im.nheko 1.0
+import "./voip"
 
 Rectangle {
     color: colors.window
@@ -10,6 +11,13 @@ Rectangle {
     Layout.preferredHeight: textInput.height
     Layout.minimumHeight: 40
 
+    Component {
+        id: placeCallDialog
+
+        PlaceCall {
+        }
+    }
+
     RowLayout {
         id: inputBar
 
@@ -28,7 +36,20 @@ Rectangle {
             Layout.topMargin: 8
             Layout.bottomMargin: 8
             Layout.leftMargin: 16
-            onClicked: TimelineManager.timeline.input.callButton()
+            onClicked: {
+                if (TimelineManager.timeline) {
+                    if (CallManager.haveCallInvite) {
+                        return;
+                    }
+                    else if (CallManager.isOnCall) {
+                        CallManager.hangUp();
+                    }
+                    else {
+                        var dialog = placeCallDialog.createObject(timelineRoot);
+                        dialog.show();
+                    }
+                }
+            }
         }
 
         ImageButton {
diff --git a/resources/qml/voip/PlaceCall.qml b/resources/qml/voip/PlaceCall.qml
new file mode 100644
index 000000000..7bf76ca67
--- /dev/null
+++ b/resources/qml/voip/PlaceCall.qml
@@ -0,0 +1,107 @@
+import QtQuick 2.3
+import QtQuick.Controls 2.3
+import QtQuick.Dialogs 1.3
+import QtQuick.Layouts 1.2
+import im.nheko 1.0
+import "../"
+
+ApplicationWindow {
+
+    flags: Qt.Dialog
+    modality: Qt.ApplicationModal
+    palette: colors
+    width: columnLayout.implicitWidth
+    height: columnLayout.implicitHeight
+
+    MessageDialog {
+        id: warningDialog
+        icon: StandardIcon.Warning
+    }
+
+    ColumnLayout {
+
+        id: columnLayout
+        spacing: 16
+
+        RowLayout {
+
+            Layout.topMargin: 16
+            Layout.leftMargin: 8
+
+            Label {
+                font.pointSize: fontMetrics.font.pointSize * 1.1
+                text: "Place a call to " + TimelineManager.timeline.roomName + "?"
+            }
+
+            Item {
+                Layout.fillWidth: true
+            }
+        }
+
+        RowLayout {
+
+            id: rowLayout
+            Layout.leftMargin: 8
+            Layout.rightMargin: 8
+            Layout.bottomMargin: 16
+            spacing: 16
+
+            function validateMic() {
+                if (CallManager.mics.length == 0) {
+                    warningDialog.text = "No microphone found.";
+                    warningDialog.open();
+                    return false;
+                }
+                else if (!CallManager.mics.includes(Settings.microphone)) {
+                    warningDialog.text = "Unknown microphone: " + Settings.microphone;
+                    warningDialog.open();
+                    return false;
+                }
+                return true;
+            }
+
+            Avatar {
+                width: avatarSize
+                height: avatarSize
+                url: TimelineManager.timeline.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
+                displayName: TimelineManager.timeline.roomName
+            }
+
+            Button {
+                text: qsTr("Voice")
+                icon.source: "qrc:/icons/icons/ui/place-call.png"
+                onClicked: {
+                    if (rowLayout.validateMic()) {
+                        CallManager.sendInvite(TimelineManager.timeline.roomId(), false);
+                        close();
+                    }
+                }
+            }
+
+            Button {
+                visible: CallManager.cameras.length > 0
+                text: qsTr("Video")
+                icon.source: "qrc:/icons/icons/ui/video-call.png"
+                onClicked: {
+                    if (rowLayout.validateMic()) {
+                        if (!CallManager.cameras.includes(Settings.camera)) {
+                            warningDialog.text = "Unknown camera: " + Settings.camera;
+                            warningDialog.open();
+                            return;
+                        }
+                        CallManager.sendInvite(TimelineManager.timeline.roomId(), true);
+                        close();
+                    }
+                }
+            }
+
+            Button {
+                palette: colors
+                text: qsTr("Cancel")
+                onClicked: {
+                    close();
+                }
+            }
+        }
+    }
+}
diff --git a/resources/res.qrc b/resources/res.qrc
index 321fe12e9..52157df0a 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -159,6 +159,7 @@
         <file>qml/device-verification/Success.qml</file>
         <file>qml/voip/ActiveCallBar.qml</file>
         <file>qml/voip/CallInviteBar.qml</file>
+        <file>qml/voip/PlaceCall.qml</file>
         <file>qml/voip/VideoCall.qml</file>
     </qresource>
     <qresource prefix="/media">
diff --git a/src/CallManager.h b/src/CallManager.h
index ad25fbd1b..75768ee15 100644
--- a/src/CallManager.h
+++ b/src/CallManager.h
@@ -37,7 +37,6 @@ class CallManager : public QObject
 public:
         CallManager(QObject *);
 
-        void sendInvite(const QString &roomid, bool isVideo);
         bool haveCallInvite() const { return haveCallInvite_; }
         bool isOnCall() const { return session_.state() != webrtc::State::DISCONNECTED; }
         bool isVideo() const { return isVideo_; }
@@ -52,6 +51,7 @@ public:
         void refreshTurnServer();
 
 public slots:
+        void sendInvite(const QString &roomid, bool isVideo);
         void syncEvent(const mtx::events::collections::TimelineEvents &event);
         void toggleMicMute();
         void toggleCameraView() { session_.toggleCameraView(); }
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 4e87349a6..238c93625 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -47,7 +47,6 @@
 
 #include "notifications/Manager.h"
 
-#include "dialogs/PlaceCall.h"
 #include "dialogs/ReadReceipts.h"
 #include "popups/UserMentions.h"
 #include "timeline/TimelineViewManager.h"
diff --git a/src/dialogs/PlaceCall.cpp b/src/dialogs/PlaceCall.cpp
deleted file mode 100644
index 85a398a2e..000000000
--- a/src/dialogs/PlaceCall.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-#include <QComboBox>
-#include <QLabel>
-#include <QPushButton>
-#include <QString>
-#include <QVBoxLayout>
-
-#include "ChatPage.h"
-#include "Config.h"
-#include "UserSettingsPage.h"
-#include "Utils.h"
-#include "WebRTCSession.h"
-#include "dialogs/PlaceCall.h"
-#include "ui/Avatar.h"
-
-namespace dialogs {
-
-PlaceCall::PlaceCall(const QString &callee,
-                     const QString &displayName,
-                     const QString &roomName,
-                     const QString &avatarUrl,
-                     QSharedPointer<UserSettings> settings,
-                     QWidget *parent)
-  : QWidget(parent)
-{
-        std::string errorMessage;
-        WebRTCSession *session = &WebRTCSession::instance();
-        if (!session->havePlugins(false, &errorMessage)) {
-                emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage));
-                emit close();
-                return;
-        }
-        session->refreshDevices();
-        microphones_ = session->getDeviceNames(false, settings->microphone().toStdString());
-        if (microphones_.empty()) {
-                emit ChatPage::instance()->showNotification(tr("No microphone found."));
-                emit close();
-                return;
-        }
-        cameras_ = session->getDeviceNames(true, settings->camera().toStdString());
-
-        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);
-
-        auto buttonLayout = new QHBoxLayout;
-        buttonLayout->setSpacing(15);
-        buttonLayout->setMargin(0);
-
-        QFont f;
-        f.setPointSizeF(f.pointSizeF());
-        auto avatar = new Avatar(this, QFontMetrics(f).height() * 3);
-        if (!avatarUrl.isEmpty())
-                avatar->setImage(avatarUrl);
-        else
-                avatar->setLetter(utils::firstChar(roomName));
-
-        voiceBtn_ = new QPushButton(tr("Voice"), this);
-        voiceBtn_->setIcon(QIcon(":/icons/icons/ui/place-call.png"));
-        voiceBtn_->setIconSize(QSize(iconSize_, iconSize_));
-        voiceBtn_->setDefault(true);
-
-        if (!cameras_.empty()) {
-                videoBtn_ = new QPushButton(tr("Video"), this);
-                videoBtn_->setIcon(QIcon(":/icons/icons/ui/video-call.png"));
-                videoBtn_->setIconSize(QSize(iconSize_, iconSize_));
-        }
-        cancelBtn_ = new QPushButton(tr("Cancel"), this);
-
-        buttonLayout->addWidget(avatar);
-        buttonLayout->addStretch();
-        buttonLayout->addWidget(voiceBtn_);
-        if (videoBtn_)
-                buttonLayout->addWidget(videoBtn_);
-        buttonLayout->addWidget(cancelBtn_);
-
-        QString name  = displayName.isEmpty() ? callee : displayName;
-        QLabel *label = new QLabel(tr("Place a call to ") + name + "?", this);
-
-        microphoneCombo_ = new QComboBox(this);
-        for (const auto &m : microphones_)
-                microphoneCombo_->addItem(QIcon(":/icons/icons/ui/microphone-unmute.png"),
-                                          QString::fromStdString(m));
-
-        if (videoBtn_) {
-                cameraCombo_ = new QComboBox(this);
-                for (const auto &c : cameras_)
-                        cameraCombo_->addItem(QIcon(":/icons/icons/ui/video-call.png"),
-                                              QString::fromStdString(c));
-        }
-
-        layout->addWidget(label);
-        layout->addLayout(buttonLayout);
-        layout->addStretch();
-        layout->addWidget(microphoneCombo_);
-        if (videoBtn_)
-                layout->addWidget(cameraCombo_);
-
-        connect(voiceBtn_, &QPushButton::clicked, this, [this, settings]() {
-                settings->setMicrophone(
-                  QString::fromStdString(microphones_[microphoneCombo_->currentIndex()]));
-                emit voice();
-                emit close();
-        });
-        if (videoBtn_)
-                connect(videoBtn_, &QPushButton::clicked, this, [this, settings, session]() {
-                        std::string error;
-                        if (!session->havePlugins(true, &error)) {
-                                emit ChatPage::instance()->showNotification(
-                                  QString::fromStdString(error));
-                                emit close();
-                                return;
-                        }
-                        settings->setMicrophone(
-                          QString::fromStdString(microphones_[microphoneCombo_->currentIndex()]));
-                        settings->setCamera(
-                          QString::fromStdString(cameras_[cameraCombo_->currentIndex()]));
-                        emit video();
-                        emit close();
-                });
-        connect(cancelBtn_, &QPushButton::clicked, this, [this]() {
-                emit cancel();
-                emit close();
-        });
-}
-
-}
diff --git a/src/dialogs/PlaceCall.h b/src/dialogs/PlaceCall.h
deleted file mode 100644
index e042258f3..000000000
--- a/src/dialogs/PlaceCall.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include <QSharedPointer>
-#include <QWidget>
-
-class QComboBox;
-class QPushButton;
-class QString;
-class UserSettings;
-
-namespace dialogs {
-
-class PlaceCall : public QWidget
-{
-        Q_OBJECT
-
-public:
-        PlaceCall(const QString &callee,
-                  const QString &displayName,
-                  const QString &roomName,
-                  const QString &avatarUrl,
-                  QSharedPointer<UserSettings> settings,
-                  QWidget *parent = nullptr);
-
-signals:
-        void voice();
-        void video();
-        void cancel();
-
-private:
-        const int iconSize_         = 18;
-        QPushButton *voiceBtn_      = nullptr;
-        QPushButton *videoBtn_      = nullptr;
-        QPushButton *cancelBtn_     = nullptr;
-        QComboBox *microphoneCombo_ = nullptr;
-        QComboBox *cameraCombo_     = nullptr;
-        std::vector<std::string> microphones_;
-        std::vector<std::string> cameras_;
-};
-
-}
diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index 78c8c6a3a..3cddd6136 100644
--- a/src/timeline/InputBar.cpp
+++ b/src/timeline/InputBar.cpp
@@ -13,7 +13,6 @@
 #include <mtx/responses/media.hpp>
 
 #include "Cache.h"
-#include "CallManager.h"
 #include "ChatPage.h"
 #include "CompletionProxyModel.h"
 #include "Logging.h"
@@ -25,7 +24,6 @@
 #include "UserSettingsPage.h"
 #include "UsersModel.h"
 #include "Utils.h"
-#include "dialogs/PlaceCall.h"
 #include "dialogs/PreviewUploadOverlay.h"
 #include "emoji/EmojiModel.h"
 
@@ -593,50 +591,6 @@ InputBar::showPreview(const QMimeData &source, QString path, const QStringList &
           });
 }
 
-void
-InputBar::callButton()
-{
-        auto callManager_ = ChatPage::instance()->callManager();
-        if (callManager_->haveCallInvite()) {
-                return;
-        } else if (callManager_->isOnCall()) {
-                callManager_->hangUp();
-        } else {
-                auto current_room_ = room->roomId();
-                if (auto roomInfo = cache::singleRoomInfo(current_room_.toStdString());
-                    roomInfo.member_count != 2) {
-                        ChatPage::instance()->showNotification("Calls are limited to 1:1 rooms.");
-                } else {
-                        std::vector<RoomMember> members(
-                          cache::getMembers(current_room_.toStdString()));
-                        const RoomMember &callee = members.front().user_id == utils::localUser()
-                                                     ? members.back()
-                                                     : members.front();
-                        auto dialog =
-                          new dialogs::PlaceCall(callee.user_id,
-                                                 callee.display_name,
-                                                 QString::fromStdString(roomInfo.name),
-                                                 QString::fromStdString(roomInfo.avatar_url),
-                                                 ChatPage::instance()->userSettings(),
-                                                 MainWindow::instance());
-                        connect(dialog,
-                                &dialogs::PlaceCall::voice,
-                                callManager_,
-                                [callManager_, current_room_]() {
-                                        callManager_->sendInvite(current_room_, false);
-                                });
-                        connect(dialog,
-                                &dialogs::PlaceCall::video,
-                                callManager_,
-                                [callManager_, current_room_]() {
-                                        callManager_->sendInvite(current_room_, true);
-                                });
-                        utils::centerWidget(dialog, MainWindow::instance());
-                        dialog->show();
-                }
-        }
-}
-
 void
 InputBar::startTyping()
 {
diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h
index 89ca34fe3..c729a6fc5 100644
--- a/src/timeline/InputBar.h
+++ b/src/timeline/InputBar.h
@@ -41,7 +41,6 @@ public slots:
         void updateState(int selectionStart, int selectionEnd, int cursorPosition, QString text);
         void openFileSelection();
         bool uploading() const { return uploading_; }
-        void callButton();
         void message(QString body);
 
         QObject *completerFor(QString completerName);
-- 
GitLab