From 9f155d9922a38407e0bd7932354dd4675b62b7ef Mon Sep 17 00:00:00 2001
From: agsantos <aline.gondimsantos@savoirfairelinux.com>
Date: Tue, 11 May 2021 20:02:43 -0400
Subject: [PATCH] callview: redesign changes in incoming and outcoming call
 views

Lrc verifies if we should call refuse or hangup, so now we can unify the call ending function in client.

GitLab: #408
Change-Id: I35f7ca282c6e49e669d849a140772600e501711a
---
 .vscode/settings.json                         |  10 +
 qml.qrc                                       |   5 +-
 src/calladapter.cpp                           |  11 +-
 src/calladapter.h                             |   1 -
 src/commoncomponents/AvatarImage.qml          |  13 ++
 src/commoncomponents/SpinningAnimation.qml    |  51 +++++
 src/constant/JamiStrings.qml                  |   5 +
 src/constant/JamiTheme.qml                    |  38 ++--
 src/mainview/MainView.qml                     |   4 +-
 .../components/CallOverlayButtonGroup.qml     |  29 ++-
 src/mainview/components/CallStackView.qml     |  67 +++---
 src/mainview/components/IncomingCallPage.qml  | 164 --------------
 src/mainview/components/InitialCallPage.qml   | 213 ++++++++++++++++++
 src/mainview/components/OutgoingCallPage.qml  | 119 ----------
 src/mainview/components/UserInfoCallPage.qml  | 154 -------------
 15 files changed, 358 insertions(+), 526 deletions(-)
 create mode 100644 .vscode/settings.json
 create mode 100644 src/commoncomponents/SpinningAnimation.qml
 delete mode 100644 src/mainview/components/IncomingCallPage.qml
 create mode 100644 src/mainview/components/InitialCallPage.qml
 delete mode 100644 src/mainview/components/OutgoingCallPage.qml
 delete mode 100644 src/mainview/components/UserInfoCallPage.qml

diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..3b42f1b10
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,10 @@
+{
+    "files.associations": {
+        "*.tcc": "cpp",
+        "array": "cpp",
+        "string": "cpp",
+        "string_view": "cpp",
+        "ranges": "cpp",
+        "thread": "cpp"
+    }
+}
\ No newline at end of file
diff --git a/qml.qrc b/qml.qrc
index 15cbfbd12..64af068e7 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -35,6 +35,7 @@
         <file>src/commoncomponents/PresenceIndicator.qml</file>
         <file>src/commoncomponents/AvatarImage.qml</file>
         <file>src/commoncomponents/DaemonReconnectPopup.qml</file>
+        <file>src/commoncomponents/SpinningAnimation.qml</file>
         <file>src/settingsview/SettingsView.qml</file>
         <file>src/settingsview/components/ChatviewSettings.qml</file>
         <file>src/settingsview/components/SettingsMenu.qml</file>
@@ -103,8 +104,7 @@
         <file>src/mainview/components/MessageWebViewHeader.qml</file>
         <file>src/mainview/components/AccountComboBox.qml</file>
         <file>src/mainview/components/CallStackView.qml</file>
-        <file>src/mainview/components/IncomingCallPage.qml</file>
-        <file>src/mainview/components/OutgoingCallPage.qml</file>
+        <file>src/mainview/components/InitialCallPage.qml</file>
         <file>src/mainview/components/AudioCallPage.qml</file>
         <file>src/mainview/components/CallOverlay.qml</file>
         <file>src/mainview/components/CallOverlayButtonGroup.qml</file>
@@ -127,7 +127,6 @@
         <file>src/mainview/components/RecordBox.qml</file>
         <file>src/mainview/components/SipInputPanel.qml</file>
         <file>src/mainview/components/ParticipantOverlayMenu.qml</file>
-        <file>src/mainview/components/UserInfoCallPage.qml</file>
         <file>src/mainview/js/videodevicecontextmenuitemcreation.js</file>
         <file>src/mainview/js/incomingcallpagecreation.js</file>
         <file>src/mainview/js/selectscreenwindowcreation.js</file>
diff --git a/src/calladapter.cpp b/src/calladapter.cpp
index d968c7f38..f8c6575cc 100644
--- a/src/calladapter.cpp
+++ b/src/calladapter.cpp
@@ -68,7 +68,7 @@ CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject*
     connect(systemTray_,
             &SystemTray::declineCallActivated,
             [this](const QString& accountId, const QString& convUid) {
-                refuseACall(accountId, convUid);
+                hangUpACall(accountId, convUid);
             });
 #endif
 
@@ -156,15 +156,6 @@ CallAdapter::hangUpACall(const QString& accountId, const QString& convUid)
     }
 }
 
-void
-CallAdapter::refuseACall(const QString& accountId, const QString& convUid)
-{
-    const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
-    if (!convInfo.uid.isEmpty()) {
-        lrcInstance_->getAccountInfo(accountId).callModel->refuse(convInfo.callId);
-    }
-}
-
 void
 CallAdapter::acceptACall(const QString& accountId, const QString& convUid)
 {
diff --git a/src/calladapter.h b/src/calladapter.h
index c110d974e..54b9625aa 100644
--- a/src/calladapter.h
+++ b/src/calladapter.h
@@ -49,7 +49,6 @@ public:
     Q_INVOKABLE void placeAudioOnlyCall();
     Q_INVOKABLE void placeCall();
     Q_INVOKABLE void hangUpACall(const QString& accountId, const QString& convUid);
-    Q_INVOKABLE void refuseACall(const QString& accountId, const QString& convUid);
     Q_INVOKABLE void acceptACall(const QString& accountId, const QString& convUid);
 
     Q_INVOKABLE void connectCallModel(const QString& accountId);
diff --git a/src/commoncomponents/AvatarImage.qml b/src/commoncomponents/AvatarImage.qml
index b4f6f2214..4a40cd5fd 100644
--- a/src/commoncomponents/AvatarImage.qml
+++ b/src/commoncomponents/AvatarImage.qml
@@ -71,6 +71,7 @@ Item {
     property bool showPresenceIndicator: true
     property int unreadMessagesCount: 0
     property bool enableAnimation: true
+    property bool showSpinningAnimation: false
 
     signal imageIsReady
 
@@ -207,6 +208,18 @@ Item {
         visible: showPresenceIndicator
     }
 
+    SpinningAnimation {
+        id: spinningAnimation
+
+        anchors.horizontalCenter: root.horizontalCenter
+        anchors.verticalCenter: root.verticalCenter
+
+        visible: showSpinningAnimation
+        width: Math.ceil(root.width * 1.05)
+        height: Math.ceil(root.height * 1.05)
+        z: -1
+    }
+
     Connections {
         target: ScreenInfo
 
diff --git a/src/commoncomponents/SpinningAnimation.qml b/src/commoncomponents/SpinningAnimation.qml
new file mode 100644
index 000000000..6147893af
--- /dev/null
+++ b/src/commoncomponents/SpinningAnimation.qml
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Controls.Universal 2.14
+import QtGraphicalEffects 1.12
+
+Item {
+    id: root
+
+    ConicalGradient {
+        anchors.fill: parent
+        angle: 0.0
+        gradient: Gradient {
+            GradientStop { position: 0.5; color: "transparent" }
+            GradientStop { position: 1.0; color: "white" }
+        }
+
+        RotationAnimation on angle {
+            loops: Animation.Infinite
+            duration: 1000
+            from: 0
+            to: 360
+        }
+    }
+    layer.enabled: true
+    layer.effect: OpacityMask {
+        maskSource: Rectangle {
+            width: root.height
+            height: root.height
+            radius: root.height / 2
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/constant/JamiStrings.qml b/src/constant/JamiStrings.qml
index 743f6355f..aa61f241b 100644
--- a/src/constant/JamiStrings.qml
+++ b/src/constant/JamiStrings.qml
@@ -27,6 +27,11 @@ Item {
     readonly property string appTitle: "Jami" + (UpdateManager.isCurrentVersionBeta() ? " (BETA)" : "")
 
     // Misc
+    property string accept: qsTr("Accept")
+    property string refuse: qsTr("Refuse")
+    property string endCall: qsTr("End call")
+    property string incomingAudioCallFrom: qsTr("Incoming audio call from {}")
+    property string incomingVideoCallFrom: qsTr("Incoming video call from {}")
     property string contactSearchConversation: qsTr("Find a user or search for a conversation")
     property string contactSearchInvitations: qsTr("Search your invitations")
     property string invitations: qsTr("Invitations")
diff --git a/src/constant/JamiTheme.qml b/src/constant/JamiTheme.qml
index 4f8a229c0..f7de36e3b 100644
--- a/src/constant/JamiTheme.qml
+++ b/src/constant/JamiTheme.qml
@@ -27,6 +27,11 @@ import net.jami.Enums 1.0
 Item {
     property bool darkTheme: SettingsAdapter.getAppValue(Settings.EnableDarkTheme)
 
+    // Jami theme colors
+    function rgba256(r, g, b, a) {
+        return Qt.rgba(r / 255, g / 255, b / 255, a / 100.)
+    }
+
     // General
     property color blackColor: "#000000"
     property color whiteColor: "#ffffff"
@@ -82,6 +87,10 @@ Item {
     property color buttonTintedRed: "red"
     property color buttonTintedRedHovered: "#c00"
     property color buttonTintedRedPressed: "#b00"
+    property color acceptGreen: rgba256(11, 130, 113, 100)
+    property color acceptGreenTransparency: rgba256(11, 130, 113, 56)
+    property color refuseRed: rgba256(204, 0, 34, 100)
+    property color refuseRedTransparent: rgba256(204, 0, 34, 56)
 
     property color closeButtonLighterBlack: "#4c4c4c"
 
@@ -142,22 +151,17 @@ Item {
     property color faddedFontColor: darkTheme? "#c0c0c0" : "#a0a0a0"
     property color faddedLastInteractionFontColor: darkTheme ? "#c0c0c0" : "#505050"
 
-    // Jami theme colors
-    function rgb256(r, g, b) {
-        return Qt.rgba(r / 255, g / 255, b / 255, 1.0)
-    }
-
-    property color darkGrey: rgb256(63, 63, 63)
-    property color blueLogo_: darkTheme ? whiteColor : rgb256(0, 7, 71)
-    property color lightGrey_: rgb256(242, 242, 242)
-    property color mediumGrey: rgb256(218, 219, 220)
-    property color grey_: rgb256(160, 160, 160)
-    property color red_: rgb256(251, 72, 71)
-    property color urgentOrange_: rgb256(255, 165, 0)
-    property color green_: rgb256(127, 255, 0)
-    property color presenceGreen_: rgb256(76, 217, 100)
-    property color bgSideBarDarkMode_: rgb256(24, 24, 24)
-    property color bgDarkMode_: rgb256(32, 32, 32)
+    property color darkGrey: rgba256(63, 63, 63, 100)
+    property color blueLogo_: darkTheme ? whiteColor : rgba256(0, 7, 71, 100)
+    property color lightGrey_: rgba256(242, 242, 242, 100)
+    property color mediumGrey: rgba256(218, 219, 220, 100)
+    property color grey_: rgba256(160, 160, 160, 100)
+    property color red_: rgba256(251, 72, 71, 100)
+    property color urgentOrange_: rgba256(255, 165, 0, 100)
+    property color green_: rgba256(127, 255, 0, 100)
+    property color presenceGreen_: rgba256(76, 217, 100, 100)
+    property color bgSideBarDarkMode_: rgba256(24, 24, 24, 100)
+    property color bgDarkMode_: rgba256(32, 32, 32, 100)
 
     property int fadeDuration: 150
 
@@ -182,6 +186,8 @@ Item {
     property real smartListItemHeight: 64
     property real smartListAvatarSize: 52
     property real smartListTransitionDuration: 120
+    property real avatarSizeInitialCall: 130
+    property real callButtonPreferredSize: 50
 
     property real maximumWidthSettingsView: 600
     property real settingsHeaderpreferredHeight: 64
diff --git a/src/mainview/MainView.qml b/src/mainview/MainView.qml
index 0aa36c031..14f73b4ba 100644
--- a/src/mainview/MainView.qml
+++ b/src/mainview/MainView.qml
@@ -198,10 +198,8 @@ Rectangle {
                     callStackView.showAudioCallPage()
                 else
                     callStackView.showVideoCallPage()
-            } else if (item.callState === Call.Status.INCOMING_RINGING) {
-                callStackView.showIncomingCallPage()
             } else {
-                callStackView.showOutgoingCallPage(item.callState)
+                callStackView.showInitialCallPage(item.callState)
             }
             pushCallStackView()
 
diff --git a/src/mainview/components/CallOverlayButtonGroup.qml b/src/mainview/components/CallOverlayButtonGroup.qml
index 88888e1b9..1e6eee8e3 100644
--- a/src/mainview/components/CallOverlayButtonGroup.qml
+++ b/src/mainview/components/CallOverlayButtonGroup.qml
@@ -34,7 +34,6 @@ Rectangle {
 
     // ButtonCounts here is to make sure that flow layout margin is calculated correctly,
     // since no other methods can make buttons at the layout center.
-    property int buttonPreferredSize: 48
     property var isModerator: true
     property var isSip: false
 
@@ -73,11 +72,11 @@ Rectangle {
                 // TODO: refactor with Flow if possible
                 // 6 is the number of button
                 // If ~ 500px, go into wide mode
-                if (callOverlayButtonGroup.width < (buttonPreferredSize * 6 -
+                if (callOverlayButtonGroup.width < (JamiTheme.callButtonPreferredSize * 6 -
                         callOverlayButtonGroup.spacing * 6 + 300)) {
                     return 0
                 } else {
-                    return  callOverlayButtonGroup.width / 2 - buttonPreferredSize * 1.5 -
+                    return  callOverlayButtonGroup.width / 2 - JamiTheme.callButtonPreferredSize * 1.5 -
                             callOverlayButtonGroup.spacing
                 }
             }
@@ -87,8 +86,8 @@ Rectangle {
             id: noMicButton
 
             Layout.leftMargin: 8
-            Layout.preferredWidth: buttonPreferredSize
-            Layout.preferredHeight: buttonPreferredSize
+            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
+            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
 
             pressedColor: JamiTheme.invertedPressedButtonColor
             hoveredColor: JamiTheme.invertedHoveredButtonColor
@@ -108,8 +107,8 @@ Rectangle {
         PushButton {
             id: hangUpButton
 
-            Layout.preferredWidth: buttonPreferredSize
-            Layout.preferredHeight: buttonPreferredSize
+            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
+            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
 
             pressedColor: JamiTheme.declineButtonPressedRed
             hoveredColor: JamiTheme.declineButtonHoverRed
@@ -126,8 +125,8 @@ Rectangle {
         PushButton {
             id: noVideoButton
 
-            Layout.preferredWidth: buttonPreferredSize
-            Layout.preferredHeight: buttonPreferredSize
+            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
+            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
 
             pressedColor: JamiTheme.invertedPressedButtonColor
             hoveredColor: JamiTheme.invertedHoveredButtonColor
@@ -151,8 +150,8 @@ Rectangle {
         PushButton {
             id: addToConferenceButton
 
-            Layout.preferredWidth: buttonPreferredSize
-            Layout.preferredHeight: buttonPreferredSize
+            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
+            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
             visible: !isModerator && !isSip
 
             pressedColor: JamiTheme.invertedPressedButtonColor
@@ -170,8 +169,8 @@ Rectangle {
         PushButton {
             id: chatButton
 
-            Layout.preferredWidth: buttonPreferredSize
-            Layout.preferredHeight: buttonPreferredSize
+            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
+            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
 
             pressedColor: JamiTheme.invertedPressedButtonColor
             hoveredColor: JamiTheme.invertedHoveredButtonColor
@@ -188,8 +187,8 @@ Rectangle {
         PushButton {
             id: optionsButton
 
-            Layout.preferredWidth: buttonPreferredSize
-            Layout.preferredHeight: buttonPreferredSize
+            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
+            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
             Layout.rightMargin: 8
 
             pressedColor: JamiTheme.invertedPressedButtonColor
diff --git a/src/mainview/components/CallStackView.qml b/src/mainview/components/CallStackView.qml
index b8b4eabd1..503057ad8 100644
--- a/src/mainview/components/CallStackView.qml
+++ b/src/mainview/components/CallStackView.qml
@@ -23,6 +23,9 @@ import QtQuick.Controls.Universal 2.14
 
 import net.jami.Models 1.0
 import net.jami.Adapters 1.0
+import net.jami.Constants 1.0
+
+import "../../commoncomponents"
 
 import "../js/incomingcallpagecreation.js" as IncomingCallPageCreation
 
@@ -30,8 +33,7 @@ Rectangle {
     id: callStackViewWindow
 
     enum StackNumber {
-        IncomingPageStack,
-        OutgoingPageStack,
+        InitialPageStack,
         AudioPageStack,
         VideoPageStack
     }
@@ -77,34 +79,29 @@ Rectangle {
         })
     }
 
-    function showAudioCallPage() {
-        var itemToFind = getItemFromStack(CallStackView.AudioPageStack)
-        if (!itemToFind) {
-            callStackMainView.push(audioCallPage, StackView.Immediate)
-        } else {
-            callStackMainView.pop(itemToFind, StackView.Immediate)
-        }
-        audioCallPage.updateUI(responsibleAccountId, responsibleConvUid)
-    }
-
-    function showOutgoingCallPage() {
-        var itemToFind = getItemFromStack(CallStackView.OutgoingPageStack)
+    function showInitialCallPage(callState) {
+        var itemToFind = getItemFromStack(CallStackView.InitialPageStack)
         if (!itemToFind) {
-            callStackMainView.push(outgoingCallPage, StackView.Immediate)
+            callStackMainView.push(initialCallPage, StackView.Immediate)
         } else {
             callStackMainView.pop(itemToFind, StackView.Immediate)
         }
-        outgoingCallPage.updateUI(responsibleAccountId, responsibleConvUid)
+        initialCallPage.accountConvPair = [responsibleAccountId, responsibleConvUid]
+        initialCallPage.callStatus = callState
+        if (initialCallPage.callStatus === Call.Status.INCOMING_RINGING)
+            initialCallPage.isIncoming = true
+        else
+            initialCallPage.isIncoming = false
     }
 
-    function showIncomingCallPage(accountId, convUid) {
-        var itemToFind = getItemFromStack(CallStackView.IncomingPageStack)
+    function showAudioCallPage() {
+        var itemToFind = getItemFromStack(CallStackView.AudioPageStack)
         if (!itemToFind) {
-            callStackMainView.push(incomingCallPage, StackView.Immediate)
+            callStackMainView.push(audioCallPage, StackView.Immediate)
         } else {
             callStackMainView.pop(itemToFind, StackView.Immediate)
         }
-        incomingCallPage.updateUI(responsibleAccountId, responsibleConvUid)
+        audioCallPage.updateUI(responsibleAccountId, responsibleConvUid)
     }
 
     function showVideoCallPage() {
@@ -155,9 +152,9 @@ Rectangle {
         target: CallAdapter
 
         function onCallStatusChanged(status, accountId, convUid) {
-            if (callStackMainView.currentItem.stackNumber === CallStackView.OutgoingPageStack
+            if (callStackMainView.currentItem.stackNumber === CallStackView.InitialPageStack
                     && responsibleConvUid === convUid && responsibleAccountId === accountId) {
-                outgoingCallPage.callStatus = status
+                initialCallPage.callStatus = status
             }
         }
 
@@ -179,18 +176,6 @@ Rectangle {
         visible: callStackMainView.currentItem.stackNumber === stackNumber
     }
 
-    OutgoingCallPage {
-        id: outgoingCallPage
-
-        property int stackNumber: CallStackView.OutgoingPageStack
-
-        visible: callStackMainView.currentItem.stackNumber === stackNumber
-
-        onCallCancelButtonIsClicked: {
-            CallAdapter.hangUpACall(responsibleAccountId, responsibleConvUid)
-        }
-    }
-
     VideoCallPage {
         id: videoCallPage
 
@@ -199,19 +184,19 @@ Rectangle {
         visible: callStackMainView.currentItem.stackNumber === stackNumber
     }
 
-    IncomingCallPage {
-        id: incomingCallPage
+    InitialCallPage {
+        id: initialCallPage
 
-        property int stackNumber: CallStackView.IncomingPageStack
+        property int stackNumber: CallStackView.InitialPageStack
 
-        onCallAcceptButtonIsClicked: {
+        onCallAccepted: {
             CallAdapter.acceptACall(responsibleAccountId, responsibleConvUid)
             communicationPageMessageWebView.setSendContactRequestButtonVisible(false)
             mainViewSidePanel.selectTab(SidePanelTabBar.Conversations)
         }
 
-        onCallCancelButtonIsClicked: {
-            CallAdapter.refuseACall(responsibleAccountId, responsibleConvUid)
+        onCallCanceled: {
+            CallAdapter.hangUpACall(responsibleAccountId, responsibleConvUid)
         }
 
         visible: callStackMainView.currentItem.stackNumber === stackNumber
@@ -222,6 +207,6 @@ Rectangle {
 
         anchors.fill: parent
 
-        initialItem: outgoingCallPage
+        initialItem: initialCallPage
     }
 }
diff --git a/src/mainview/components/IncomingCallPage.qml b/src/mainview/components/IncomingCallPage.qml
deleted file mode 100644
index a53e63ba8..000000000
--- a/src/mainview/components/IncomingCallPage.qml
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2020 by Savoir-faire Linux
- * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-import QtQuick 2.14
-import QtQuick.Controls 2.14
-import QtQuick.Layouts 1.14
-import QtQuick.Controls.Universal 2.14
-import Qt.labs.platform 1.1
-
-import net.jami.Models 1.0
-import net.jami.Adapters 1.0
-import net.jami.Constants 1.0
-
-import "../../commoncomponents"
-
-Rectangle {
-    id: incomingCallPage
-
-    property int buttonPreferredSize: 48
-
-    signal callCancelButtonIsClicked
-    signal callAcceptButtonIsClicked
-
-    color: "black"
-
-    function updateUI(accountId, convUid) {
-        userInfoIncomingCallPage.updateUI(accountId, convUid)
-    }
-
-    // Prevent right click propagate to VideoCallPage.
-    MouseArea {
-        anchors.fill: parent
-        propagateComposedEvents: false
-        acceptedButtons: Qt.AllButtons
-        onDoubleClicked: mouse.accepted = true
-    }
-
-    ColumnLayout {
-        id: incomingCallPageColumnLayout
-
-        anchors.fill: parent
-
-        // Common elements with OutgoingCallPage
-        UserInfoCallPage {
-            id: userInfoIncomingCallPage
-            Layout.fillWidth: true
-            Layout.fillHeight: true
-        }
-
-        Text {
-            id: talkToYouText
-
-            Layout.alignment: Qt.AlignCenter
-            Layout.preferredWidth: incomingCallPage.width
-            Layout.preferredHeight: 32
-
-            font.pointSize: JamiTheme.textFontSize
-
-            horizontalAlignment: Text.AlignHCenter
-            verticalAlignment: Text.AlignVCenter
-            color: "white"
-
-            text: JamiStrings.isCallingYou
-        }
-
-        RowLayout {
-            id: incomingCallPageRowLayout
-
-            Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
-            Layout.bottomMargin: 48
-            Layout.topMargin: 48
-
-            Layout.preferredWidth: incomingCallPage.width - 200
-            Layout.maximumWidth: 200
-            Layout.preferredHeight: buttonPreferredSize
-
-            ColumnLayout {
-                id: callAnswerButtonColumnLayout
-
-                Layout.alignment: Qt.AlignLeft
-
-                PushButton {
-                    id: callAnswerButton
-
-                    Layout.alignment: Qt.AlignCenter
-
-                    Layout.preferredWidth: buttonPreferredSize
-                    Layout.preferredHeight: buttonPreferredSize
-
-                    pressedColor: JamiTheme.acceptButtonPressedGreen
-                    hoveredColor: JamiTheme.acceptButtonHoverGreen
-                    normalColor: JamiTheme.acceptButtonGreen
-
-                    source: "qrc:/images/icons/check-24px.svg"
-                    imageColor: JamiTheme.whiteColor
-
-                    onClicked: callAcceptButtonIsClicked()
-                }
-            }
-
-            ColumnLayout {
-                id: callDeclineButtonColumnLayout
-
-                Layout.alignment: Qt.AlignRight
-
-                PushButton {
-                    id: callDeclineButton
-
-                    Layout.alignment: Qt.AlignCenter
-
-                    Layout.preferredWidth: buttonPreferredSize
-                    Layout.preferredHeight: buttonPreferredSize
-
-                    pressedColor: JamiTheme.declineButtonPressedRed
-                    hoveredColor: JamiTheme.declineButtonHoverRed
-                    normalColor: JamiTheme.declineButtonRed
-
-                    source: "qrc:/images/icons/round-close-24px.svg"
-                    imageColor: JamiTheme.whiteColor
-
-                    toolTipText: JamiStrings.hangup
-
-                    onClicked: callCancelButtonIsClicked()
-                }
-            }
-        }
-    }
-
-    Shortcut {
-        sequence: "Ctrl+Y"
-        context: Qt.ApplicationShortcut
-        onActivated: {
-            incomingCallPage.close()
-            CallAdapter.acceptACall(responsibleAccountId,
-                                    responsibleConvUid)
-            communicationPageMessageWebView.setSendContactRequestButtonVisible(false)
-        }
-    }
-
-    Shortcut {
-        sequence: "Ctrl+Shift+D"
-        context: Qt.ApplicationShortcut
-        onActivated: {
-            incomingCallPage.close()
-            CallAdapter.refuseACall(responsibleAccountId,
-                                    responsibleConvUid)
-        }
-    }
-}
diff --git a/src/mainview/components/InitialCallPage.qml b/src/mainview/components/InitialCallPage.qml
new file mode 100644
index 000000000..19b934f26
--- /dev/null
+++ b/src/mainview/components/InitialCallPage.qml
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2020-2021 by Savoir-faire Linux
+ * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
+ *         Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Controls.Universal 2.14
+import Qt.labs.platform 1.1
+
+import net.jami.Models 1.0
+import net.jami.Adapters 1.0
+import net.jami.Constants 1.0
+
+import "../../commoncomponents"
+
+Rectangle {
+    id: root
+
+    property bool isIncoming: false
+    property bool isAudioOnly: false
+    property var accountConvPair: ["",""]
+    property int callStatus: 0
+    property string bestName: ""
+
+    signal callCanceled
+    signal callAccepted
+
+    color: "black"
+
+    ListModel {
+        id: incomingControlsModel
+        ListElement { type: "refuse"; image: "qrc:/images/icons/round-close-24px.svg"}
+        ListElement { type: "accept"; image: "qrc:/images/icons/check-24px.svg"}
+    }
+    ListModel {
+        id: outgoingControlsModel
+        ListElement { type: "cancel"; image: "qrc:/images/icons/round-close-24px.svg"}
+    }
+
+    onAccountConvPairChanged: {
+        if (accountConvPair[1]) {
+            contactImg.updateImage(accountConvPair[1])
+            root.bestName = UtilsAdapter.getBestName(accountConvPair[0], accountConvPair[1])
+        }
+    }
+
+    // Prevent right click propagate to VideoCallPage.
+    MouseArea {
+        anchors.fill: parent
+        propagateComposedEvents: false
+        acceptedButtons: Qt.AllButtons
+        onDoubleClicked: mouse.accepted = true
+    }
+
+    ColumnLayout {
+        anchors.horizontalCenter: root.horizontalCenter
+        anchors.verticalCenter: root.verticalCenter
+
+        AvatarImage {
+            id: contactImg
+
+            Layout.alignment: Qt.AlignHCenter
+            Layout.preferredWidth: JamiTheme.avatarSizeInitialCall
+            Layout.preferredHeight: JamiTheme.avatarSizeInitialCall
+
+            mode: AvatarImage.Mode.FromConvUid
+            showPresenceIndicator: false
+            showSpinningAnimation: true
+        }
+
+        Text {
+            Layout.alignment: Qt.AlignHCenter
+            Layout.preferredWidth: JamiTheme.preferredFieldWidth
+            Layout.topMargin: 32
+
+            font.pointSize: JamiTheme.titleFontSize
+
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+
+            text: {
+                if (root.isIncoming)
+                    return root.isAudioOnly ? JamiStrings.incomingAudioCallFrom.replace("{}", root.bestName) : JamiStrings.incomingVideoCallFrom.replace("{}", root.bestName)
+                else
+                    return root.bestName
+            }
+            wrapMode: Text.WordWrap
+            elide: Text.ElideRight
+            maximumLineCount: root.isIncoming ? 2 : 1
+            color: "white"
+        }
+
+        Text {
+            Layout.alignment: Qt.AlignHCenter
+            Layout.preferredWidth: root.width
+            Layout.topMargin: 8
+
+            font.pointSize: JamiTheme.smartlistItemFontSize
+
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+
+            text: UtilsAdapter.getCallStatusStr(callStatus) + "…"
+            color: JamiTheme.whiteColor
+            visible: !root.isIncoming
+        }
+
+        RowLayout {
+            Layout.alignment: Qt.AlignHCenter
+            Layout.topMargin: 32
+
+            Repeater {
+                id: controlButtons
+                model: root.isIncoming ? incomingControlsModel : outgoingControlsModel
+
+                delegate: ColumnLayout {
+                    PushButton {
+                        Layout.leftMargin: 10
+                        Layout.rightMargin: 10
+                        Layout.alignment: Qt.AlignHCenter
+                        implicitWidth: JamiTheme.callButtonPreferredSize
+                        implicitHeight: JamiTheme.callButtonPreferredSize
+
+                        pressedColor: {
+                            if (type === "accept" )
+                                return JamiTheme.acceptGreen
+                            return JamiTheme.refuseRed
+                        }
+                        hoveredColor: {
+                            if (type === "accept" )
+                                return JamiTheme.acceptGreen
+                            return JamiTheme.refuseRed
+                        }
+                        normalColor: {
+                            if (type === "accept" )
+                                return JamiTheme.acceptGreenTransparency
+                            return JamiTheme.refuseRedTransparent
+                        }
+
+                        source: image
+                        imageColor: JamiTheme.whiteColor
+
+                        onClicked: {
+                            if (type === "accept")
+                                callAccepted()
+                            else
+                                callCanceled()
+                        }
+                    }
+
+                    Label {
+                        Layout.alignment: Qt.AlignHCenter
+                        Layout.preferredWidth: JamiTheme.callButtonPreferredSize
+                        Layout.preferredHeight: JamiTheme.preferredFieldHeight
+
+                        font.pointSize: JamiTheme.smartlistItemInfoFontSize
+                        font.kerning: true
+                        color: JamiTheme.whiteColor
+
+                        text: {
+                            if (type === "refuse")
+                                return JamiStrings.refuse
+                            else if (type === "accept")
+                                return JamiStrings.accept
+                            else if (type === "cancel")
+                                return JamiStrings.endCall
+                            return ""
+                        }
+
+                        horizontalAlignment: Text.AlignHCenter
+                        verticalAlignment: Text.AlignVCenter
+                        wrapMode: Text.WordWrap
+                    }
+                }
+            }
+        }
+    }
+
+    Shortcut {
+        sequence: "Ctrl+Y"
+        context: Qt.ApplicationShortcut
+        onActivated: {
+            CallAdapter.acceptACall(root.accountConvPair[0],
+                                    root.accountConvPair[1])
+            communicationPageMessageWebView.setSendContactRequestButtonVisible(false)
+        }
+    }
+
+    Shortcut {
+        sequence: "Ctrl+Shift+D"
+        context: Qt.ApplicationShortcut
+        onActivated: {
+            CallAdapter.hangUpACall(root.accountConvPair[0],
+                                    root.accountConvPair[1])
+        }
+    }
+}
diff --git a/src/mainview/components/OutgoingCallPage.qml b/src/mainview/components/OutgoingCallPage.qml
deleted file mode 100644
index 72023a0dc..000000000
--- a/src/mainview/components/OutgoingCallPage.qml
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2020 by Savoir-faire Linux
- * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-import QtQuick 2.14
-import QtQuick.Controls 2.14
-import QtQuick.Layouts 1.14
-import QtQuick.Controls.Universal 2.14
-
-import net.jami.Models 1.0
-import net.jami.Adapters 1.0
-import net.jami.Constants 1.0
-
-import "../../commoncomponents"
-
-Rectangle {
-    id: outgoingCallPageRect
-
-    property int buttonPreferredSize: 50
-    property int callStatus: 0
-    signal callCancelButtonIsClicked
-
-    function updateUI(accountId, convUid) {
-        userInfoCallPage.updateUI(accountId, convUid)
-    }
-
-    anchors.fill: parent
-
-    color: "black"
-
-    // Prevent right click propagate to VideoCallPage.
-    MouseArea {
-        anchors.fill: parent
-        propagateComposedEvents: false
-        acceptedButtons: Qt.AllButtons
-        onDoubleClicked: mouse.accepted = true
-    }
-
-    ColumnLayout {
-        id: outgoingCallPageRectColumnLayout
-
-        anchors.fill: parent
-
-        UserInfoCallPage {
-            id: userInfoCallPage
-            Layout.fillHeight: true
-            Layout.fillWidth: true
-        }
-
-        AnimatedImage {
-            id: spinnerImage
-
-            Layout.alignment: Qt.AlignCenter
-            Layout.preferredWidth: 24
-            Layout.preferredHeight: 8
-
-            source: "qrc:/images/waiting.gif"
-        }
-
-        Text {
-            id: callStatusText
-
-            Layout.alignment: Qt.AlignCenter
-            Layout.preferredWidth: outgoingCallPageRect.width
-            Layout.preferredHeight: 30
-
-            font.pointSize: JamiTheme.textFontSize
-
-            horizontalAlignment: Text.AlignHCenter
-            verticalAlignment: Text.AlignVCenter
-
-            text: UtilsAdapter.getCallStatusStr(callStatus) + "…"
-            color: Qt.lighter("white", 1.5)
-        }
-
-        ColumnLayout {
-            id: callCancelButtonColumnLayout
-
-            Layout.alignment: Qt.AlignCenter
-            Layout.bottomMargin: 48
-
-            PushButton {
-                id: callCancelButton
-
-                Layout.alignment: Qt.AlignCenter
-
-                Layout.preferredWidth: buttonPreferredSize
-                Layout.preferredHeight: buttonPreferredSize
-
-                pressedColor: JamiTheme.declineButtonPressedRed
-                hoveredColor: JamiTheme.declineButtonHoverRed
-                normalColor: JamiTheme.declineButtonRed
-
-                source: "qrc:/images/icons/round-close-24px.svg"
-                imageColor: JamiTheme.whiteColor
-
-                toolTipText: JamiStrings.hangup
-
-                onClicked: {
-                    callCancelButtonIsClicked()
-                }
-            }
-        }
-    }
-}
diff --git a/src/mainview/components/UserInfoCallPage.qml b/src/mainview/components/UserInfoCallPage.qml
deleted file mode 100644
index 133d8a0d7..000000000
--- a/src/mainview/components/UserInfoCallPage.qml
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2020 by Savoir-faire Linux
- * Author: Albert Babí <albert.babi@savoirfairelinux.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-import QtQuick 2.14
-import QtQuick.Controls 2.14
-import QtQuick.Layouts 1.14
-import QtQuick.Controls.Universal 2.14
-
-import net.jami.Models 1.0
-import net.jami.Adapters 1.0
-import net.jami.Constants 1.0
-
-import "../../commoncomponents"
-
-// Common element for IncomingCallPage and OutgoingCallPage
-Rectangle {
-    id: userInfoCallRect
-
-    property int buttonPreferredSize: 48
-    property string bestName: "Best Name"
-    property string bestId: "Best Id"
-
-    function updateUI(accountId, convUid) {
-        contactImg.updateImage(convUid)
-        bestName = UtilsAdapter.getBestName(accountId, convUid)
-        var id = UtilsAdapter.getBestId(accountId, convUid)
-        bestId = (bestName !== id) ? id : ""
-    }
-
-    color: "black"
-
-    ColumnLayout {
-        id: userInfoCallColumnLayout
-
-        anchors.fill: parent
-
-        PushButton {
-            id: backButton
-
-            Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
-            Layout.preferredWidth: JamiTheme.preferredFieldHeight
-            Layout.preferredHeight: JamiTheme.preferredFieldHeight
-            Layout.rightMargin: JamiTheme.preferredMarginSize
-            Layout.topMargin: JamiTheme.preferredMarginSize
-            Layout.leftMargin: JamiTheme.preferredMarginSize
-
-            source: "qrc:/images/icons/ic_arrow_back_24px.svg"
-
-            pressedColor: JamiTheme.invertedPressedButtonColor
-            hoveredColor: JamiTheme.invertedHoveredButtonColor
-            normalColor: JamiTheme.invertedNormalButtonColor
-
-            imageColor: JamiTheme.whiteColor
-
-            toolTipText: qsTr("Toggle to display side panel")
-
-            visible: mainView.sidePanelOnly
-
-            onClicked: mainView.showWelcomeView()
-        }
-
-        AvatarImage {
-            id: contactImg
-
-            Layout.alignment: Qt.AlignCenter
-            Layout.topMargin: 48
-
-            Layout.preferredWidth: 100
-            Layout.preferredHeight: 100
-
-            mode: AvatarImage.Mode.FromConvUid
-            showPresenceIndicator: false
-        }
-
-        Rectangle {
-            id: userInfoCallPageTextRect
-
-            Layout.alignment: Qt.AlignCenter
-            Layout.topMargin: 8
-
-            Layout.preferredWidth: userInfoCallRect.width
-            Layout.preferredHeight: jamiBestNameText.height + jamiBestIdText.height + 100
-
-            color: "transparent"
-
-            ColumnLayout {
-                id: userInfoCallPageTextRectColumnLayout
-
-                Text {
-                    id: jamiBestNameText
-
-                    Layout.alignment: Qt.AlignCenter
-                    Layout.preferredWidth: userInfoCallPageTextRect.width
-                    Layout.preferredHeight: 48
-
-                    font.pointSize: JamiTheme.headerFontSize
-
-                    horizontalAlignment: Text.AlignHCenter
-                    verticalAlignment: Text.AlignVCenter
-
-                    text: textMetricsjamiBestNameText.elidedText
-                    color: "white"
-
-                    TextMetrics {
-                        id: textMetricsjamiBestNameText
-                        font: jamiBestNameText.font
-                        text: bestName
-                        elideWidth: userInfoCallPageTextRect.width - 48
-                        elide: Qt.ElideMiddle
-                    }
-                }
-
-                Text {
-                    id: jamiBestIdText
-
-                    Layout.alignment: Qt.AlignCenter
-                    Layout.preferredWidth: userInfoCallPageTextRect.width
-                    Layout.preferredHeight: 32
-
-                    font.pointSize: JamiTheme.textFontSize
-
-                    horizontalAlignment: Text.AlignHCenter
-                    verticalAlignment: Text.AlignVCenter
-
-                    text: textMetricsjamiBestIdText.elidedText
-                    color: Qt.lighter("white", 1.5)
-
-                    TextMetrics {
-                        id: textMetricsjamiBestIdText
-                        font: jamiBestIdText.font
-                        text: bestId
-                        elideWidth: userInfoCallPageTextRect.width - 48
-                        elide: Qt.ElideMiddle
-                    }
-                }
-            }
-        }
-    }
-}
-- 
GitLab