From be70e36bf74bf734ebeefc2e0f18cf3b61df687b Mon Sep 17 00:00:00 2001 From: ababi <albert.babi@savoirfairelinux.com> Date: Mon, 31 Aug 2020 13:17:44 +0200 Subject: [PATCH] mainview: call flow review - incoming / outgoing call UI management - add system native notifications - incoming call page same model as outgoing - UserInfoCallPage is used to display common information for incoming / outgoing calls Gitlab: #32 Change-Id: If33196a30c51698b4edad55cb8f718066034e422 --- images/icons/arrow_back-white-24dp.svg | 1 + images/icons/close-white-24dp.svg | 1 + images/icons/done-white-24dp.svg | 1 + qml.qrc | 1 + resources.qrc | 1 + src/calladapter.cpp | 161 ++++++--- src/calladapter.h | 8 +- src/globalsystemtray.cpp | 10 +- src/globalsystemtray.h | 6 +- src/mainview/MainView.qml | 191 +++++----- src/mainview/components/CallOverlay.qml | 25 ++ src/mainview/components/CallStackView.qml | 57 ++- src/mainview/components/IncomingCallPage.qml | 337 ++++-------------- src/mainview/components/OutgoingCallPage.qml | 133 ++----- src/mainview/components/SidePanel.qml | 5 +- src/mainview/components/UserInfoCallPage.qml | 154 ++++++++ .../CurrentAccountSettingsScrollPage.qml | 4 +- src/smartlistmodel.cpp | 3 +- src/utils.h | 28 +- src/utilsadapter.cpp | 34 ++ src/utilsadapter.h | 3 + 21 files changed, 615 insertions(+), 549 deletions(-) create mode 100644 images/icons/arrow_back-white-24dp.svg create mode 100644 images/icons/close-white-24dp.svg create mode 100644 images/icons/done-white-24dp.svg create mode 100644 src/mainview/components/UserInfoCallPage.qml diff --git a/images/icons/arrow_back-white-24dp.svg b/images/icons/arrow_back-white-24dp.svg new file mode 100644 index 000000000..bc39c0465 --- /dev/null +++ b/images/icons/arrow_back-white-24dp.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="24px" height="24px"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg> \ No newline at end of file diff --git a/images/icons/close-white-24dp.svg b/images/icons/close-white-24dp.svg new file mode 100644 index 000000000..14dc9e497 --- /dev/null +++ b/images/icons/close-white-24dp.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="24px" height="24px"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg> \ No newline at end of file diff --git a/images/icons/done-white-24dp.svg b/images/icons/done-white-24dp.svg new file mode 100644 index 000000000..f835a956e --- /dev/null +++ b/images/icons/done-white-24dp.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="24px" height="24px"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg> \ No newline at end of file diff --git a/qml.qrc b/qml.qrc index e4d6e5009..ffd949492 100644 --- a/qml.qrc +++ b/qml.qrc @@ -112,5 +112,6 @@ <file>src/wizardview/components/AccountCreationStepIndicator.qml</file> <file>src/commoncomponents/SpinnerButton.qml</file> <file>src/commoncomponents/UsernameLineEdit.qml</file> + <file>src/mainview/components/UserInfoCallPage.qml</file> </qresource> </RCC> diff --git a/resources.qrc b/resources.qrc index 5a4127787..0ea0cf0b8 100644 --- a/resources.qrc +++ b/resources.qrc @@ -119,5 +119,6 @@ <file>images/icons/person_add-24px.svg</file> <file>images/icons/router-24px.svg</file> <file>images/icons/insert_drive_file-24dp.svg</file> + <file>images/icons/arrow_back-white-24dp.svg</file> </qresource> </RCC> diff --git a/src/calladapter.cpp b/src/calladapter.cpp index 97835d5e0..c49ce85e2 100644 --- a/src/calladapter.cpp +++ b/src/calladapter.cpp @@ -23,10 +23,10 @@ */ #include "calladapter.h" - -#include "globalsystemtray.h" #include "utils.h" +#include <QApplication> + CallAdapter::CallAdapter(QObject* parent) : QmlAdapterBase(parent) , oneSecondTimer_(new QTimer(this)) @@ -94,7 +94,6 @@ CallAdapter::refuseACall(const QString& accountId, const QString& convUid) void CallAdapter::acceptACall(const QString& accountId, const QString& convUid) { - emit incomingCallNeedToSetupMainView(accountId, convUid); auto* convModel = LRCInstance::getCurrentConversationModel(); const auto convInfo = convModel->getConversationForUID(convUid); if (!convInfo.uid.isEmpty()) { @@ -107,50 +106,35 @@ CallAdapter::acceptACall(const QString& accountId, const QString& convUid) void CallAdapter::slotShowIncomingCallView(const QString& accountId, const conversation::Info& convInfo) { + auto selectedAccountId = LRCInstance::getCurrAccId(); auto* callModel = LRCInstance::getCurrentCallModel(); - if (!callModel->hasCall(convInfo.callId)) { - /* - * Connection to close potential incoming call page when it is not current account. - */ - auto& accInfo = LRCInstance::accountModel().getAccountInfo(accountId); - - QObject::disconnect(closeIncomingCallPageConnection_); - - closeIncomingCallPageConnection_ - = QObject::connect(accInfo.callModel.get(), - &lrc::api::NewCallModel::callStatusChanged, - [this, accountId, uid = convInfo.uid](const QString& callId) { - auto& accInfo = LRCInstance::accountModel().getAccountInfo( - accountId); - auto& callModel = accInfo.callModel; - auto call = callModel->getCall(callId); - - switch (call.status) { - case lrc::api::call::Status::INVALID: - case lrc::api::call::Status::INACTIVE: - case lrc::api::call::Status::ENDED: - case lrc::api::call::Status::PEER_BUSY: - case lrc::api::call::Status::TIMEOUT: - case lrc::api::call::Status::TERMINATING: { - if (!uid.isEmpty()) - emit closePotentialIncomingCallPageWindow(accountId, uid); - break; - } - default: - break; - } - - emit updateConversationSmartList(); - QObject::disconnect(closeIncomingCallPageConnection_); - }); - /* - * Show incoming call page only. - */ - auto accountProperties = LRCInstance::accountModel().getAccountConfig(accountId); - if (!accountProperties.autoAnswer && !accountProperties.isRendezVous) { - emit showIncomingCallPage(accountId, convInfo.uid); + + if (QApplication::focusObject() == nullptr || accountId != selectedAccountId) { + showNotification(accountId, convInfo); + return; } + + auto* convModel = LRCInstance::getCurrentConversationModel(); + const auto currentConvUid = LRCInstance::getCurrentConvUid(); + const auto currentConvInfo = convModel->getConversationForUID(currentConvUid); + + // Call in current conversation + auto currentConvHasCall = callModel->hasCall(currentConvInfo.callId); + + // Check INCOMING / OUTGOING call in current conversation + if (currentConvHasCall) { + auto currentCall = callModel->getCall(currentConvInfo.callId); + if (currentCall.status == lrc::api::call::Status::CONNECTED || + currentCall.status == lrc::api::call::Status::IN_PROGRESS) { + showNotification(accountId, convInfo); + return; + } + } + emit incomingCallNeedToSetupMainView(accountId, convInfo.uid); + emit showIncomingCallPage(accountId, convInfo.uid); + emit showCallStack(accountId, convInfo.uid, true); + emit updateConversationSmartList(); return; } @@ -163,15 +147,55 @@ CallAdapter::slotShowIncomingCallView(const QString& accountId, const conversati emit showCallStack(accountId, convInfo.uid); } } else { - auto selectedAccountId = LRCInstance::getCurrentAccountInfo().id; + auto showIncomingCall = false; auto accountProperties = LRCInstance::accountModel().getAccountConfig(selectedAccountId); if (!accountProperties.autoAnswer && !accountProperties.isRendezVous) { + + // App not focused or in different account + if (QApplication::focusObject() == nullptr || accountId != selectedAccountId) { + showNotification(accountId, convInfo); + return; + } + + auto* convModel = LRCInstance::getCurrentConversationModel(); + const auto currentConvUid = LRCInstance::getCurrentConvUid(); + const auto currentConvInfo = convModel->getConversationForUID(currentConvUid); + + // Call in current conversation + auto currentConvHasCall = callModel->hasCall(currentConvInfo.callId); + + // Check INCOMING / OUTGOING call in current conversation + if (isCallSelected) { + if (currentConvHasCall) { + auto currentCall = callModel->getCall(currentConvInfo.callId); + if (currentCall.status == lrc::api::call::Status::OUTGOING_RINGING) { + showNotification(accountId, convInfo); + } else { + showIncomingCall = true; + } + } else { + showIncomingCall = true; + } + } else { // Not current conversation + if (currentConvHasCall) { + auto currentCall = callModel->getCall(currentConvInfo.callId); + if (currentCall.status == lrc::api::call::Status::CONNECTED || + currentCall.status == lrc::api::call::Status::IN_PROGRESS) { + showNotification(accountId, convInfo); + return; + } + } + showIncomingCall = true; + } + } + + if (showIncomingCall) { + emit incomingCallNeedToSetupMainView(accountId, convInfo.uid); emit showIncomingCallPage(accountId, convInfo.uid); + emit showCallStack(accountId, convInfo.uid, true); } } - emit callStatusChanged(static_cast<int>(call.status), accountId, convInfo.uid); - emit updateConversationSmartList(); } @@ -275,6 +299,48 @@ CallAdapter::getConferencesInfos() return map; } +void +CallAdapter::showNotification(const QString& accountId, const lrc::api::conversation::Info& convInfo) +{ + // Hack for handling multiple consecutive calls to slotShowIncomingCallView (bug) + // Do not set notification if it is already active for the account and conversation + if (accountId == GlobalSystemTray::instance().getTriggeredAccountId() && + convInfo.uid == GlobalSystemTray::instance().getPossibleOnGoingConversationUid()) { + return; + } + + QString sender = convInfo.uid; + if (accountId != "") { + auto& accInfo = LRCInstance::getAccountInfo(accountId); + if (!convInfo.participants.isEmpty()) { + auto &contact = accInfo.contactModel->getContact(convInfo.participants[0]); + sender = Utils::bestNameForContact(contact); + } + } + + GlobalSystemTray::instance().setPossibleOnGoingConversationUid(convInfo.uid); + + QObject::connect(&GlobalSystemTray::instance(), &GlobalSystemTray::messageClicked, + this, [this, accountId, convInfo]() { + if (accountId != "" && convInfo.uid != "") { + + emit incomingCallNeedToSetupMainView(accountId, convInfo.uid, true); + + auto call = LRCInstance::getCallInfoForConversation(convInfo); + if (call->status == lrc::api::call::Status::INCOMING_RINGING) { + emit showIncomingCallPage(accountId, convInfo.uid); + emit showCallStack(accountId, convInfo.uid, true); + } + emit updateConversationSmartList(); + } + GlobalSystemTray::instance().setTriggeredAccountId(""); + GlobalSystemTray::instance().setPossibleOnGoingConversationUid(""); + + }, Qt::UniqueConnection); + + Utils::showSystemNotification(QApplication::focusWidget(), sender, tr("is calling you"), 0, accountId); +} + void CallAdapter::connectCallModel(const QString& accountId) { @@ -385,7 +451,6 @@ CallAdapter::connectCallModel(const QString& accountId) } } else { emit closeCallStack(accountId, convInfo.uid); - emit closePotentialIncomingCallPageWindow(accountId, convInfo.uid); } break; diff --git a/src/calladapter.h b/src/calladapter.h index 498f45705..cfc18edc9 100644 --- a/src/calladapter.h +++ b/src/calladapter.h @@ -21,10 +21,12 @@ #include "lrcinstance.h" #include "qmladapterbase.h" +#include "globalsystemtray.h" #include <QObject> #include <QString> #include <QVariant> +#include <QSystemTrayIcon> class CallAdapter final : public QmlAdapterBase { @@ -70,14 +72,14 @@ signals: void showVideoCallPage(const QString& accountId, const QString& convUid, const QString& callId); void showCallStack(const QString& accountId, const QString& convUid, bool forceReset = false); void closeCallStack(const QString& accountId, const QString& convUid); - void closePotentialIncomingCallPageWindow(const QString& accountId, const QString& convUid); void callStatusChanged(int index, const QString& accountId, const QString& convUid); void updateConversationSmartList(); void updateParticipantsInfos(const QVariantList& infos, const QString& accountId, const QString& callId); - void incomingCallNeedToSetupMainView(const QString& accountId, const QString& convUid); + void incomingCallNeedToSetupMainView(const QString& accountId, const QString& convUid, + bool fromNotification = false); void previewVisibilityNeedToChange(bool visible); /* @@ -105,6 +107,7 @@ private: const QString& accountId = {}, bool forceCallOnly = false); bool shouldShowPreview(bool force); + void showNotification(const QString& accountId, const lrc::api::conversation::Info& convInfo); /* * Current conf/call info. @@ -115,6 +118,7 @@ private: QMetaObject::Connection callStatusChangedConnection_; QMetaObject::Connection onParticipantsChangedConnection_; QMetaObject::Connection closeIncomingCallPageConnection_; + QMetaObject::Connection appStateChangedConnection_; /* * For Call Overlay diff --git a/src/globalsystemtray.cpp b/src/globalsystemtray.cpp index ae98ff291..73b9fddbb 100644 --- a/src/globalsystemtray.cpp +++ b/src/globalsystemtray.cpp @@ -33,13 +33,13 @@ GlobalSystemTray::getTriggeredAccountId() } void -GlobalSystemTray::setPossibleOnGoingConversationInfo(const lrc::api::conversation::Info& convInfo) +GlobalSystemTray::setPossibleOnGoingConversationUid(const QString& convUid) { - triggeredOnGoingConvInfo_ = convInfo; + triggeredOnGoingConvUid_ = convUid; } -const lrc::api::conversation::Info& -GlobalSystemTray::getPossibleOnGoingConversationInfo() +const QString& +GlobalSystemTray::getPossibleOnGoingConversationUid() { - return triggeredOnGoingConvInfo_; + return triggeredOnGoingConvUid_; } diff --git a/src/globalsystemtray.h b/src/globalsystemtray.h index 3c84808e5..1ce513c29 100644 --- a/src/globalsystemtray.h +++ b/src/globalsystemtray.h @@ -41,13 +41,13 @@ public: const QString& getTriggeredAccountId(); - void setPossibleOnGoingConversationInfo(const lrc::api::conversation::Info& convInfo); + void setPossibleOnGoingConversationUid(const QString& convUid); - const lrc::api::conversation::Info& getPossibleOnGoingConversationInfo(); + const QString& getPossibleOnGoingConversationUid(); private: GlobalSystemTray(); QString triggeredAccountId_; - lrc::api::conversation::Info triggeredOnGoingConvInfo_; + QString triggeredOnGoingConvUid_; }; diff --git a/src/mainview/MainView.qml b/src/mainview/MainView.qml index 26f66a91f..9085882a1 100644 --- a/src/mainview/MainView.qml +++ b/src/mainview/MainView.qml @@ -25,7 +25,6 @@ import net.jami.Models 1.0 import net.jami.Adapters 1.0 // Import qml component files. - import "components" import "../wizardview" import "../settingsview" @@ -47,38 +46,76 @@ Window { property int savedSidePanelViewMaxWidth: 0 property int savedWelcomeViewMinWidth: 0 property int savedWelcomeViewMaxWidth: 0 - property bool sidePanelHidden: false + property bool sidePanelHidden: !mainViewStack.visible // To calculate tab bar bottom border hidden rect left margin. property int tabBarLeftMargin: 8 property int tabButtonShrinkSize: 8 property bool inSettingsView: false - property bool needToShowCallStack: false - property bool needToCloseCallStack: false signal closeApp signal noAccountIsAvailable - function pushCallStackView(){ - if (mainViewStack.visible) { + function showWelcomeView() { + mainViewWindowSidePanel.deselectConversationSmartList() + if (communicationPageMessageWebView.visible || callStackView.visible) { + sidePanelViewStack.pop(StackView.Immediate) + if (!sidePanelHidden) { + mainViewStack.pop(welcomePage, StackView.Immediate) + } + } + } + + function setCallStackView() { + + mainViewWindowSidePanel.deselectConversationSmartList() + + var currentAccount = AccountAdapter.currentAccountId + var currentCallConv = UtilsAdapter.getCallConvForAccount(currentAccount) + + ConversationsAdapter.selectConversation(currentCallConv) + var callId = UtilsAdapter.getCallId(currentAccount, currentCallConv) + var callStatus = UtilsAdapter.getCallStatus(callId) + + switch (callStatus) { + case Call.Status.INCOMING_RINGING: + callStackView.showIncomingCallPage(currentAccount, currentCallConv) + break + case Call.Status.OUTGOING_RINGING: + callStackView.showOutgoingCallPage() + break + default: + if (UtilsAdapter.hasVideoCall()) { + callStackView.showVideoCallPage(callId) + } else { + callStackView.showAudioCallPage() + } + } + + pushCallStackView() + + callStackView.responsibleAccountId = currentAccount + callStackView.responsibleConvUid = currentCallConv + callStackView.updateCorrespondingUI() + } + + + function pushCallStackView() { + if (sidePanelHidden) { + sidePanelViewStack.push(callStackView, StackView.Immediate) + } else { + sidePanelViewStack.pop(StackView.Immediate) mainViewStack.pop(null, StackView.Immediate) mainViewStack.push(callStackView, StackView.Immediate) - } else { - sidePanelViewStack.pop(null, StackView.Immediate) - sidePanelViewStack.push(callStackView, StackView.Immediate) } } - function pushCommunicationMessageWebView(){ - if (mainViewStack.visible) { - mainViewStack.pop(null, StackView.Immediate) - mainViewStack.push(communicationPageMessageWebView, - StackView.Immediate) + function pushCommunicationMessageWebView() { + if (sidePanelHidden) { + sidePanelViewStack.push(communicationPageMessageWebView, StackView.Immediate) } else { - sidePanelViewStack.pop(null, StackView.Immediate) - sidePanelViewStack.push( - communicationPageMessageWebView, - StackView.Immediate) + mainViewStack.pop(null, StackView.Immediate) + mainViewStack.push(communicationPageMessageWebView, StackView.Immediate) } } @@ -86,6 +123,10 @@ Window { mainViewWindowSidePanel.refreshAccountComboBox(index) } + function currentAccountIsCalling() { + return UtilsAdapter.hasCall(AccountAdapter.currentAccountId) + } + function recursionStackViewItemMove(stackOne, stackTwo, depth=1) { // Move all items (expect the bottom item) to stacktwo by the same order in stackone. @@ -111,28 +152,25 @@ Window { mainViewStack.push(settingsView, StackView.Immediate) sidePanelViewStack.push(leftPanelSettingsView, StackView.Immediate) } - ConversationsAdapter.disconnectConversationModel() } else { - ConversationsAdapter.connectConversationModel(false) - ConversationsAdapter.refill() // to be sure to have latest informations - mainViewWindowSidePanel.forceUpdateConversationSmartListView() - - if (!sidePanelHidden) { - sidePanelViewStack.pop(mainViewWindowSidePanel, StackView.Immediate) - mainViewStack.pop(StackView.Immediate) - } else { + if (sidePanelHidden) { recursionStackViewItemMove(sidePanelViewStack, mainViewStack, 2) sidePanelViewStack.pop(StackView.Immediate) mainViewStack.pop(StackView.Immediate) recursionStackViewItemMove(mainViewStack, sidePanelViewStack, 1) } - if (needToCloseCallStack) { - pushCommunicationMessageWebView() - needToShowCallStack = false - needToCloseCallStack = false + if (currentAccountIsCalling()) { + setCallStackView() + } else { + mainViewWindowSidePanel.deselectConversationSmartList() + + sidePanelViewStack.pop(StackView.Immediate) + if (!sidePanelHidden) { + mainViewStack.pop(welcomePage, StackView.Immediate) + } } } inSettingsView = !inSettingsView @@ -150,13 +188,11 @@ Window { function onShowCallStack(accountId, convUid, forceReset) { - needToShowCallStack = true if (forceReset) { callStackView.responsibleAccountId = accountId callStackView.responsibleConvUid = convUid } - // Check if it is coming from the current responsible call, // and push views onto the correct stackview if (callStackView.responsibleAccountId === accountId @@ -166,7 +202,6 @@ Window { } function onCloseCallStack(accountId, convUid) { - // Check if call stack view is on any of the stackview. if (callStackView.responsibleAccountId === accountId && callStackView.responsibleConvUid === convUid) { @@ -174,20 +209,16 @@ Window { return item.objectName === "callStackViewObject" }) || sidePanelViewStack.find(function (item, index) { return item.objectName === "callStackViewObject" - }) || (inSettingsView && needToShowCallStack)) { - callStackView.needToCloseInCallConversationAndPotentialWindow() - + })) { if (!inSettingsView) { + callStackView.needToCloseInCallConversationAndPotentialWindow() pushCommunicationMessageWebView() - needToShowCallStack = false - } else { - needToCloseCallStack = true } } } } - function onIncomingCallNeedToSetupMainView(accountId, convUid) { + function onIncomingCallNeedToSetupMainView(accountId, convUid, fromNotification) { // Set up the call stack view that is needed by call overlay. if (!inSettingsView) { @@ -205,16 +236,14 @@ Window { communicationPageMessageWebView.headerUserUserNameLabelText = (name !== id) ? id : "" callStackView.needToCloseInCallConversationAndPotentialWindow() - callStackView.setLinkedWebview( - communicationPageMessageWebView) + callStackView.setLinkedWebview(communicationPageMessageWebView) callStackView.responsibleAccountId = accountId callStackView.responsibleConvUid = convUid - callStackView.updateCorrspondingUI() + callStackView.updateCorrespondingUI() mainViewWindowSidePanel.refreshAccountComboBox(index) - ConversationsAdapter.selectConversation(accountId, convUid) - + ConversationsAdapter.selectConversation(accountId, convUid, !fromNotification) MessagesAdapter.setupChatView(convUid) } } @@ -264,7 +293,9 @@ Window { Rectangle { implicitWidth: 1 implicitHeight: splitView.height - color: SplitHandle.pressed ? JamiTheme.pressColor : (SplitHandle.hovered ? JamiTheme.hoverColor : JamiTheme.tabbarBorderColor) + color: SplitHandle.pressed ? JamiTheme.pressColor : + (SplitHandle.hovered ? JamiTheme.hoverColor : + JamiTheme.tabbarBorderColor) } } @@ -272,9 +303,10 @@ Window { id: mainViewSidePanelRect SplitView.minimumWidth: sidePanelViewStackPreferredWidth SplitView.maximumWidth: (sidePanelHidden ? splitView.width : - splitView.width - sidePanelViewStackPreferredWidth) + splitView.width - sidePanelViewStackPreferredWidth) SplitView.fillHeight: true + // AccountComboBox is always visible AccountComboBox { id: accountComboBox @@ -304,24 +336,26 @@ Window { } onAccountChanged: { + mainViewWindowSidePanel.deselectConversationSmartList() + mainViewWindowSidePanel.refreshAccountComboBox(index) + settingsView.slotAccountListChanged() settingsView.setSelected(settingsView.selectedMenu, true) - if (needToShowCallStack - && callStackView.responsibleAccountId === UtilsAdapter.getCurrAccId()){ - if (!AccountAdapter.hasVideoCall()) { - pushCommunicationMessageWebView() - needToShowCallStack = false - } else if (needToShowCallStack) { - pushCallStackView() + if (!inSettingsView) { + if (currentAccountIsCalling()) { + setCallStackView() + } else { + showWelcomeView() } } } onNeedToBackToWelcomePage: { - if (!inSettingsView) + if (!inSettingsView && !currentAccountIsCalling()) { mainViewWindowSidePanel.accountComboBoxNeedToShowWelcomePage() + } } onNewAccountButtonClicked: { @@ -338,7 +372,8 @@ Window { initialItem: mainViewWindowSidePanel - anchors.top: accountComboBox.visible ? accountComboBox.bottom : mainViewSidePanelRect.top + anchors.top: accountComboBox.visible ? accountComboBox.bottom : + mainViewSidePanelRect.top width: mainViewSidePanelRect.width height: accountComboBox.visible ? mainViewSidePanelRect.height - accountComboBox.height : mainViewSidePanelRect.height @@ -352,7 +387,8 @@ Window { initialItem: welcomePage - SplitView.maximumWidth: sidePanelHidden ? splitView.width : splitView.width - sidePanelViewStackPreferredWidth + SplitView.maximumWidth: sidePanelHidden ? splitView.width : + splitView.width - sidePanelViewStackPreferredWidth SplitView.minimumWidth: sidePanelViewStackPreferredWidth SplitView.fillHeight: true @@ -410,7 +446,6 @@ Window { } } - SidePanel { id: mainViewWindowSidePanel @@ -422,11 +457,11 @@ Window { callStackView.needToCloseInCallConversationAndPotentialWindow() callStackView.responsibleAccountId = UtilsAdapter.getCurrAccId() callStackView.responsibleConvUid = currentUID - callStackView.updateCorrspondingUI() + callStackView.updateCorrespondingUI() if (callStackViewShouldShow) { if (callState === Call.Status.IN_PROGRESS || callState === Call.Status.PAUSED) { - UtilsAdapter.setCurrentCall(UtilsAdapter.getCurrAccId(), currentUID) + UtilsAdapter.setCurrentCall(AccountAdapter.currentAccountId, currentUID) if (isAudioOnly) callStackView.showAudioCallPage() else @@ -434,15 +469,17 @@ Window { UtilsAdapter.getCallId( callStackView.responsibleAccountId, callStackView.responsibleConvUid)) + } else if (callState === Call.Status.INCOMING_RINGING) { + callStackView.showIncomingCallPage(AccountAdapter.currentAccountId, + currentUID) } else { - callStackView.showOutgoingCallPage(callStateStr) + callStackView.showOutgoingCallPage() } } // Set up chatview. MessagesAdapter.setupChatView(currentUID) - callStackView.setLinkedWebview( - communicationPageMessageWebView) + callStackView.setLinkedWebview(communicationPageMessageWebView) if (mainViewStack.find(function (item, index) { return item.objectName === "communicationPageMessageWebView" @@ -453,7 +490,6 @@ Window { return } - // Push messageWebView or callStackView onto the correct stackview mainViewStack.pop(null, StackView.Immediate) sidePanelViewStack.pop(null, StackView.Immediate) @@ -483,13 +519,13 @@ Window { onAccountComboBoxNeedToShowWelcomePage: { // If the item argument is specified, all items down to (but not including) item will be popped. - if (!inSettingsView) { - mainViewStack.pop(welcomePage) + if (!inSettingsView && !currentAccountIsCalling()) { + showWelcomeView() } } onConversationSmartListViewNeedToShowWelcomePage: { - mainViewStack.pop(welcomePage) + showWelcomeView() } onNeedToUpdateConversationForAddedContact: { @@ -559,22 +595,15 @@ Window { } onNeedToGoBackToWelcomeView: { - mainViewWindowSidePanel.deselectConversationSmartList() - if (communicationPageMessageWebView.visible - && !mainViewStack.visible) { - sidePanelViewStack.pop() - } else if (communicationPageMessageWebView.visible - && mainViewStack.visible) { - mainViewStack.pop() - } + showWelcomeView() recordBox.visible = false } Component.onCompleted: { sidePanelViewStack.SplitView.maximumWidth = Qt.binding(function() { - return (hiddenView ? splitView.width : - splitView.width - sidePanelViewStackPreferedWidth) + return (sidePanelHidden ? splitView.width : + splitView.width - sidePanelViewStackPreferredWidth) }) recordBox.x = Qt.binding(function() { @@ -592,7 +621,6 @@ Window { sidePanelViewStack.height + recordBox.y_offset }) - // Set qml MessageWebView object pointer to c++. MessagesAdapter.setQmlObject(this) } @@ -604,7 +632,6 @@ Window { + mainViewStackPreferredWidth - 5 && mainViewStack.visible) { mainViewStack.visible = false - sidePanelHidden = true // The find callback function is called for each item in the stack. var inWelcomeViewStack = mainViewStack.find( @@ -621,7 +648,6 @@ Window { + mainViewStackPreferredWidth + 5 && !mainViewStack.visible) { mainViewStack.visible = true - sidePanelHidden = false var inSidePanelViewStack = sidePanelViewStack.find( function (item, index) { @@ -631,6 +657,9 @@ Window { recursionStackViewItemMove(sidePanelViewStack, mainViewStack, (inSettingsView ? 2 : 1)) } + if (!inSettingsView && currentAccountIsCalling()) + pushCallStackView() + mainViewWindow.update() } } diff --git a/src/mainview/components/CallOverlay.qml b/src/mainview/components/CallOverlay.qml index ed5653ef7..764a50d36 100644 --- a/src/mainview/components/CallOverlay.qml +++ b/src/mainview/components/CallOverlay.qml @@ -151,6 +151,31 @@ Rectangle { anchors.fill: parent + HoverableButton { + 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 + + radius: 32 + source: "qrc:/images/icons/arrow_back-white-24dp.svg" + backgroundColor: "transparent" + onExitColor: "transparent" + onEnterColor: JamiTheme.lightGrey_ + toolTipText: qsTr("Toggle to display side panel") + hoverEnabled: true + + visible: mainViewWindow.sidePanelHidden + + onClicked: { + mainViewWindow.showWelcomeView() // TODO: refactor with msg manager + } + } + Text { id: jamiBestNameText diff --git a/src/mainview/components/CallStackView.qml b/src/mainview/components/CallStackView.qml index 5b20feef8..38301eecd 100644 --- a/src/mainview/components/CallStackView.qml +++ b/src/mainview/components/CallStackView.qml @@ -32,6 +32,12 @@ Rectangle { anchors.fill: parent + Shortcut { + sequence: "Ctrl+D" + context: Qt.ApplicationShortcut + onActivated: CallAdapter.hangUpThisCall() + } + // When selected conversation is changed, // these values will also be changed. property string responsibleConvUid: "" @@ -41,7 +47,6 @@ Rectangle { audioCallPage.closeInCallConversation() videoCallPage.closeInCallConversation() - // Close potential window, context menu releated windows. audioCallPage.closeContextMenuAndRelatedWindows() @@ -54,9 +59,10 @@ Rectangle { videoCallPage.setLinkedWebview(webViewId) } - function updateCorrspondingUI() { + function updateCorrespondingUI() { audioCallPage.updateUI(responsibleAccountId, responsibleConvUid) outgoingCallPage.updateUI(responsibleAccountId, responsibleConvUid) + incomingCallPage.updateUI(responsibleAccountId, responsibleConvUid) videoCallPage.updateUI(responsibleAccountId, responsibleConvUid) } @@ -73,7 +79,7 @@ Rectangle { audioCallPage.updateUI(responsibleAccountId, responsibleConvUid) } - function showOutgoingCallPage(currentCallStatus) { + function showOutgoingCallPage() { var itemToFind = callStackMainView.find(function (item) { return item.stackNumber === 1 }) @@ -83,8 +89,21 @@ Rectangle { } else { callStackMainView.pop(itemToFind, StackView.Immediate) } - if (currentCallStatus) - outgoingCallPage.callStatus = currentCallStatus + } + + function showIncomingCallPage(accountId, convUid) { + var itemToFind = callStackMainView.find(function (item) { + return item.stackNumber === 3 + }) + + if (!itemToFind) { + callStackMainView.push(incomingCallPage, StackView.Immediate) + } else { + callStackMainView.pop(itemToFind, StackView.Immediate) + } + responsibleAccountId = accountId + responsibleConvUid = convUid + incomingCallPage.updateUI(accountId, convUid) } function showVideoCallPage(callId) { @@ -106,7 +125,6 @@ Rectangle { function onShowOutgoingCallPage(accountId, convUid) { - // Need to check whether it is the current selected conversation. if (responsibleConvUid === convUid && responsibleAccountId === accountId) { @@ -115,18 +133,7 @@ Rectangle { } function onShowIncomingCallPage(accountId, convUid) { - - - // Check is done within the js. - IncomingCallPageCreation.createincomingCallPageWindowObjects( - accountId, convUid) - IncomingCallPageCreation.showIncomingCallPageWindow(accountId, - convUid) - } - - function onClosePotentialIncomingCallPageWindow(accountId, convUid) { - IncomingCallPageCreation.closeIncomingCallPageWindow(accountId, - convUid) + showIncomingCallPage(accountId, convUid) } function onShowAudioCallPage(accountId, convUid) { @@ -196,6 +203,20 @@ Rectangle { } } + IncomingCallPage { + id: incomingCallPage + + property int stackNumber: 3 + + onCallAcceptButtonIsClicked: { + CallAdapter.acceptACall(responsibleAccountId, responsibleConvUid) + } + + onCallCancelButtonIsClicked: { + CallAdapter.hangUpACall(responsibleAccountId, responsibleConvUid) + } + } + StackView { id: callStackMainView diff --git a/src/mainview/components/IncomingCallPage.qml b/src/mainview/components/IncomingCallPage.qml index 87d68eea6..e01e40757 100644 --- a/src/mainview/components/IncomingCallPage.qml +++ b/src/mainview/components/IncomingCallPage.qml @@ -25,313 +25,128 @@ import net.jami.Models 1.0 import "../../commoncomponents" - -// IncomingCallPage as a seperate window, -// exist at the right bottom, as a notification to user that -// a call is incoming. -Window { +Rectangle { id: incomingCallPage - property int minWidth: 300 - property int minHeight: 400 - + property int buttonPreferredSize: 48 - // The unique identifier for incomingCallPage - property string responsibleAccountId: "" - property string responsibleConvUid: "" + signal callCancelButtonIsClicked + signal callAcceptButtonIsClicked - property string contactImgSource: "" - property string bestName: "Best Name" - property string bestId: "Best Id" + color: "black" - property int buttonPreferredSize: 50 - property variant clickPos: "1,1" - - function updateUI() { - incomingCallPage.contactImgSource = "data:image/png;base64," - + UtilsAdapter.getContactImageString(responsibleAccountId, - responsibleConvUid) - incomingCallPage.bestName = UtilsAdapter.getBestName( - responsibleAccountId, responsibleConvUid) - var id = UtilsAdapter.getBestId(responsibleAccountId, - responsibleConvUid) - incomingCallPage.bestId = (incomingCallPage.bestName !== id) ? id : "" + function updateUI(accountId, convUid) { + userInfoIncomingCallPage.updateUI(accountId, convUid) } - function updatePositionToRightBottom() { - - - // Screen right bottom, - // since the qt screen.virtualY, virtualX does not work properly, - // we need to calculate the screen x, y ourselves, by - // using to fact that window will always be in the middle if no x or y - // specificed. - // ex: https://doc.qt.io/qt-5/qscreen.html#geometry-prop - var virtualX = (incomingCallPage.x + width / 2) - screen.width / 2 - incomingCallPage.x = virtualX + screen.width - width - incomingCallPage.y = screen.height - height - 50 + // Prevent right click propagate to VideoCallPage. + MouseArea { + anchors.fill: parent + propagateComposedEvents: false + acceptedButtons: Qt.RightButton } - minimumWidth: minWidth - minimumHeight: minHeight - - maximumWidth: minWidth + 300 - maximumHeight: minHeight + 300 - - flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint - screen: Qt.application.screens[0] - - Rectangle { - id: incomingCallPageColumnLayoutMainRect + ColumnLayout { + id: incomingCallPageColumnLayout anchors.fill: parent - radius: 15 - color: "black" - - - // Simulate window drag. (top with height 30). - MouseArea { - id: dragMouseArea - - anchors.left: incomingCallPageColumnLayoutMainRect.left - anchors.top: incomingCallPageColumnLayoutMainRect.top - - width: incomingCallPageColumnLayoutMainRect.width - closeButton.width - 10 - height: 30 - - onPressed: { - clickPos = Qt.point(mouse.x, mouse.y) - } - - onPositionChanged: { - var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y) - incomingCallPage.x += delta.x - incomingCallPage.y += delta.y - } + // Common elements with OutgoingCallPage + UserInfoCallPage { + id: userInfoIncomingCallPage + Layout.fillWidth: true + Layout.fillHeight: true } - HoverableButton { - id: closeButton - - anchors.top: incomingCallPageColumnLayoutMainRect.top - anchors.topMargin: 10 - anchors.right: incomingCallPageColumnLayoutMainRect.right - anchors.rightMargin: 10 - - width: 30 - height: 30 - - radius: 30 - source: "qrc:/images/icons/ic_close_white_24dp.png" - backgroundColor: "black" - onEnterColor: JamiTheme.closeButtonLighterBlack - onExitColor: "black" - onPressColor: JamiTheme.declineButtonPressedRed - onReleaseColor: "black" - - onClicked: { - incomingCallPage.close() - CallAdapter.refuseACall(responsibleAccountId, - responsibleConvUid) - } - } - - ColumnLayout { - id: incomingCallPageColumnLayout - - anchors.fill: parent - - Image { - id: contactImg - - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 30 - - Layout.preferredWidth: 100 - Layout.preferredHeight: 100 - - fillMode: Image.PreserveAspectFit - source: contactImgSource - } - - Rectangle { - id: incomingCallPageTextRect - - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 5 - - Layout.preferredWidth: incomingCallPage.width - Layout.preferredHeight: jamiBestNameText.height + jamiBestIdText.height - + talkToYouText.height + 20 + Text { + id: talkToYouText - ColumnLayout { - id: incomingCallPageTextRectColumnLayout + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: incomingCallPage.width + Layout.preferredHeight: 32 - Text { - id: jamiBestNameText + font.pointSize: JamiTheme.textFontSize - Layout.alignment: Qt.AlignCenter - Layout.preferredWidth: incomingCallPageTextRect.width - Layout.preferredHeight: 50 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: "white" - font.pointSize: JamiTheme.textFontSize + 3 - - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - - text: textMetricsjamiBestNameText.elidedText - color: "white" - - TextMetrics { - id: textMetricsjamiBestNameText - font: jamiBestNameText.font - text: bestName - elideWidth: incomingCallPageTextRect.width - 30 - elide: Qt.ElideMiddle - } - } + text: "is calling you" + } - Text { - id: jamiBestIdText + RowLayout { + id: incomingCallPageRowLayout - Layout.alignment: Qt.AlignCenter - Layout.preferredWidth: incomingCallPageTextRect.width - Layout.preferredHeight: 30 + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + Layout.bottomMargin: 48 + Layout.topMargin: 48 - font.pointSize: JamiTheme.textFontSize + Layout.preferredWidth: incomingCallPage.width - 200 + Layout.maximumWidth: 200 + Layout.preferredHeight: buttonPreferredSize - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter + ColumnLayout { + id: callAnswerButtonColumnLayout - text: textMetricsjamiBestIdText.elidedText - color: "white" + Layout.alignment: Qt.AlignLeft - TextMetrics { - id: textMetricsjamiBestIdText - font: jamiBestIdText.font - text: bestId - elideWidth: incomingCallPageTextRect.width - 30 - elide: Qt.ElideMiddle - } - } + HoverableButton { + id: callAnswerButton - Text { - id: talkToYouText + Layout.alignment: Qt.AlignCenter - Layout.alignment: Qt.AlignCenter - Layout.preferredWidth: incomingCallPageTextRect.width - Layout.preferredHeight: 30 + Layout.preferredWidth: buttonPreferredSize + Layout.preferredHeight: buttonPreferredSize - font.pointSize: JamiTheme.textFontSize + backgroundColor: JamiTheme.acceptButtonGreen + onEnterColor: JamiTheme.acceptButtonHoverGreen + onPressColor: JamiTheme.acceptButtonPressedGreen + onReleaseColor: JamiTheme.acceptButtonHoverGreen + onExitColor: JamiTheme.acceptButtonGreen - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: "white" + buttonImageHeight: buttonPreferredSize / 2 + buttonImageWidth: buttonPreferredSize / 2 + source: "qrc:/images/icons/ic_check_white_18dp_2x.png" + radius: 32 - text: "is calling you" + onClicked: { + callAcceptButtonIsClicked() } } - - color: "transparent" } - RowLayout { - id: incomingCallPageRowLayout - - Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom - Layout.bottomMargin: 5 + ColumnLayout { + id: callDeclineButtonColumnLayout - Layout.preferredWidth: incomingCallPage.width - 100 - Layout.preferredHeight: buttonPreferredSize + Layout.alignment: Qt.AlignRight - ColumnLayout { - id: callAnswerButtonColumnLayout + HoverableButton { + id: callDeclineButton - Layout.alignment: Qt.AlignLeft + Layout.alignment: Qt.AlignCenter - HoverableButton { - id: callAnswerButton + Layout.preferredWidth: buttonPreferredSize + Layout.preferredHeight: buttonPreferredSize - Layout.alignment: Qt.AlignCenter + backgroundColor: JamiTheme.declineButtonRed + onEnterColor: JamiTheme.declineButtonHoverRed + onPressColor: JamiTheme.declineButtonPressedRed + onReleaseColor: JamiTheme.declineButtonHoverRed + onExitColor: JamiTheme.declineButtonRed - Layout.preferredWidth: buttonPreferredSize - Layout.preferredHeight: buttonPreferredSize + buttonImageHeight: buttonPreferredSize / 2 + buttonImageWidth: buttonPreferredSize / 2 + source: "qrc:/images/icons/ic_close_white_24dp.png" + radius: 32 - backgroundColor: JamiTheme.acceptButtonGreen - onEnterColor: JamiTheme.acceptButtonHoverGreen - onPressColor: JamiTheme.acceptButtonPressedGreen - onReleaseColor: JamiTheme.acceptButtonHoverGreen - onExitColor: JamiTheme.acceptButtonGreen - - buttonImageHeight: buttonPreferredSize / 2 - buttonImageWidth: buttonPreferredSize / 2 - source: "qrc:/images/icons/ic_check_white_18dp_2x.png" - radius: 30 - - onClicked: { - incomingCallPage.close() - CallAdapter.acceptACall(responsibleAccountId, - responsibleConvUid) - } - } - - Text { - id: answerText - - Layout.alignment: Qt.AlignCenter - - font.pointSize: JamiTheme.textFontSize - 2 - text: qsTr("Answer") - } - } - - ColumnLayout { - id: callDeclineButtonColumnLayout - - Layout.alignment: Qt.AlignRight - - HoverableButton { - id: callDeclineButton - - Layout.alignment: Qt.AlignCenter - - Layout.preferredWidth: buttonPreferredSize - Layout.preferredHeight: buttonPreferredSize - - backgroundColor: JamiTheme.declineButtonRed - onEnterColor: JamiTheme.declineButtonHoverRed - onPressColor: JamiTheme.declineButtonPressedRed - onReleaseColor: JamiTheme.declineButtonHoverRed - onExitColor: JamiTheme.declineButtonRed - - buttonImageHeight: buttonPreferredSize / 2 - buttonImageWidth: buttonPreferredSize / 2 - source: "qrc:/images/icons/ic_close_white_24dp.png" - radius: 30 - - onClicked: { - incomingCallPage.close() - CallAdapter.refuseACall(responsibleAccountId, - responsibleConvUid) - } - } - - Text { - id: ignoreText - - Layout.alignment: Qt.AlignCenter - - font.pointSize: JamiTheme.textFontSize - 2 - text: qsTr("Ignore") + onClicked: { + callCancelButtonIsClicked() } } } } } - color: "transparent" - Shortcut { sequence: "Ctrl+Y" context: Qt.ApplicationShortcut diff --git a/src/mainview/components/OutgoingCallPage.qml b/src/mainview/components/OutgoingCallPage.qml index 5acd4032d..457f8f407 100644 --- a/src/mainview/components/OutgoingCallPage.qml +++ b/src/mainview/components/OutgoingCallPage.qml @@ -30,25 +30,16 @@ Rectangle { property int buttonPreferredSize: 50 property int callStatus: 0 - property string contactImgSource: "" - property string bestName: "Best Name" - property string bestId: "Best Id" - signal callCancelButtonIsClicked function updateUI(accountId, convUid) { - contactImgSource = "data:image/png;base64," + UtilsAdapter.getContactImageString( - accountId, convUid) - bestName = UtilsAdapter.getBestName(accountId, convUid) - var id = UtilsAdapter.getBestId(accountId, convUid) - bestId = (bestName !== id) ? id : "" + userInfoCallPage.updateUI(accountId, convUid) } anchors.fill: parent color: "black" - // Prevent right click propagate to VideoCallPage. MouseArea { anchors.fill: parent @@ -61,114 +52,43 @@ Rectangle { anchors.fill: parent - Image { - id: contactImg + UserInfoCallPage { + id: userInfoCallPage + Layout.fillHeight: true + Layout.fillWidth: true + } - Layout.alignment: Qt.AlignCenter + AnimatedImage { + id: spinnerImage - Layout.preferredWidth: 100 - Layout.preferredHeight: 100 + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: 24 + Layout.preferredHeight: 8 - fillMode: Image.PreserveAspectFit - source: contactImgSource - asynchronous: true + source: "qrc:/images/waiting.gif" } - Rectangle { - id: outgoingCallPageTextRect + Text { + id: callStatusText Layout.alignment: Qt.AlignCenter - Layout.topMargin: 5 - Layout.preferredWidth: outgoingCallPageRect.width - Layout.preferredHeight: jamiBestNameText.height + jamiBestIdText.height - + callStatusText.height + spinnerImage.height + 20 - - color: "transparent" - - ColumnLayout { - id: outgoingCallPageTextRectColumnLayout - - Text { - id: jamiBestNameText - - Layout.alignment: Qt.AlignCenter - Layout.preferredWidth: outgoingCallPageTextRect.width - Layout.preferredHeight: 50 - - font.pointSize: JamiTheme.textFontSize + 3 - - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - - text: textMetricsjamiBestNameText.elidedText - color: "white" - - TextMetrics { - id: textMetricsjamiBestNameText - font: jamiBestNameText.font - text: bestName - elideWidth: outgoingCallPageTextRect.width - 50 - elide: Qt.ElideMiddle - } - } - - Text { - id: jamiBestIdText - - Layout.alignment: Qt.AlignCenter - Layout.preferredWidth: outgoingCallPageTextRect.width - Layout.preferredHeight: 30 - - font.pointSize: JamiTheme.textFontSize + Layout.preferredHeight: 30 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter + font.pointSize: JamiTheme.textFontSize - text: textMetricsjamiBestIdText.elidedText - color: Qt.lighter("white", 1.5) + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter - TextMetrics { - id: textMetricsjamiBestIdText - font: jamiBestIdText.font - text: bestId - elideWidth: outgoingCallPageTextRect.width - 50 - elide: Qt.ElideMiddle - } - } - - AnimatedImage { - id: spinnerImage - - Layout.alignment: Qt.AlignCenter - Layout.preferredWidth: 20 - Layout.preferredHeight: 5 - - source: "qrc:/images/waiting.gif" - } - - Text { - id: callStatusText - - Layout.alignment: Qt.AlignCenter - Layout.preferredWidth: outgoingCallPageTextRect.width - Layout.preferredHeight: 30 - - font.pointSize: JamiTheme.textFontSize - - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - - text: UtilsAdapter.getCallStatusStr(callStatus) + "..." - color: Qt.lighter("white", 1.5) - } - } + text: UtilsAdapter.getCallStatusStr(callStatus) + "..." + color: Qt.lighter("white", 1.5) } ColumnLayout { id: callCancelButtonColumnLayout Layout.alignment: Qt.AlignCenter + Layout.bottomMargin: 48 HoverableButton { id: callCancelButton @@ -192,18 +112,9 @@ Rectangle { toolTipText: qsTr("Cancel the call") onClicked: { - outgoingCallPageRect.callCancelButtonIsClicked() + callCancelButtonIsClicked() } } - - Text { - id: cancelText - - Layout.alignment: Qt.AlignCenter - - font.pointSize: JamiTheme.textFontSize - 2 - text: qsTr("Cancel") - } } } } diff --git a/src/mainview/components/SidePanel.qml b/src/mainview/components/SidePanel.qml index c7e169c32..bc9520d8d 100644 --- a/src/mainview/components/SidePanel.qml +++ b/src/mainview/components/SidePanel.qml @@ -32,7 +32,7 @@ Rectangle { property int pendingRequestCount: 0 property int totalUnreadMessagesCount: 0 - signal conversationSmartListNeedToAccessMessageWebView(string currentUserDisplayName, string currentUserAlias, string currentUID, bool callStackViewShouldShow, bool isAudioOnly, string callState) + signal conversationSmartListNeedToAccessMessageWebView(string currentUserDisplayName, string currentUserAlias, string currentUID, bool callStackViewShouldShow, bool isAudioOnly, int callState) signal accountComboBoxNeedToShowWelcomePage() signal conversationSmartListViewNeedToShowWelcomePage signal needToUpdateConversationForAddedContact @@ -188,8 +188,7 @@ Rectangle { target: ConversationsAdapter function onShowChatView(accountId, convUid) { - conversationSmartListView.needToShowChatView(accountId, - convUid) + conversationSmartListView.needToShowChatView(accountId, convUid) } function onShowConversationTabs(visible) { diff --git a/src/mainview/components/UserInfoCallPage.qml b/src/mainview/components/UserInfoCallPage.qml new file mode 100644 index 000000000..1e18bed2f --- /dev/null +++ b/src/mainview/components/UserInfoCallPage.qml @@ -0,0 +1,154 @@ +/* + * 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.12 +import net.jami.Models 1.0 +import net.jami.Adapters 1.0 + +import "../../commoncomponents" + +// Common element for IncomingCallPage and OutgoingCallPage +Rectangle { + id: userInfoCallRect + + property int buttonPreferredSize: 48 + property string contactImgSource: "" + property string bestName: "Best Name" + property string bestId: "Best Id" + + function updateUI(accountId, convUid) { + contactImgSource = "data:image/png;base64," + UtilsAdapter.getContactImageString( + accountId, convUid) + bestName = UtilsAdapter.getBestName(accountId, convUid) + var id = UtilsAdapter.getBestId(accountId, convUid) + bestId = (bestName !== id) ? id : "" + } + + color: "black" + + ColumnLayout { + id: userInfoCallColumnLayout + + anchors.fill: parent + + HoverableButton { + 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 + + radius: 32 + source: "qrc:/images/icons/arrow_back-white-24dp.svg" + backgroundColor: "transparent" + onExitColor: "transparent" + onEnterColor: JamiTheme.lightGrey_ + toolTipText: qsTr("Toggle to display side panel") + hoverEnabled: true + + visible: mainViewWindow.sidePanelHidden + + onClicked: { + mainViewWindow.showWelcomeView() // TODO: refactor with msg manager + } + } + + Image { + id: contactImg + + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 48 + + Layout.preferredWidth: 100 + Layout.preferredHeight: 100 + + fillMode: Image.PreserveAspectFit + source: contactImgSource + asynchronous: true + } + + 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 + } + } + } + } + } +} diff --git a/src/settingsview/components/CurrentAccountSettingsScrollPage.qml b/src/settingsview/components/CurrentAccountSettingsScrollPage.qml index 337056395..968243462 100644 --- a/src/settingsview/components/CurrentAccountSettingsScrollPage.qml +++ b/src/settingsview/components/CurrentAccountSettingsScrollPage.qml @@ -112,7 +112,7 @@ Rectangle { Connections { id: accountConnections_ContactModel target: AccountAdapter.contactModel - enabled: accountViewRect.visible + enabled: root.visible function onModelUpdated(uri, needsSorted) { updateAndShowBannedContactsSlot() @@ -130,7 +130,7 @@ Rectangle { Connections { id: accountConnections_DeviceModel target: AccountAdapter.deviceModel - enabled: accountViewRect.visible + enabled: root.visible function onDeviceAdded(id) { updateAndShowDevicesSlot() diff --git a/src/smartlistmodel.cpp b/src/smartlistmodel.cpp index 33a1194d3..1e3d5964c 100644 --- a/src/smartlistmodel.cpp +++ b/src/smartlistmodel.cpp @@ -335,7 +335,8 @@ SmartListModel::getConversationItemData(const conversation::Info& item, return QVariant(callModel->hasCall(convInfo.callId) && ((!call.isOutgoing && (call.status == lrc::api::call::Status::IN_PROGRESS - || call.status == lrc::api::call::Status::PAUSED)) + || call.status == lrc::api::call::Status::PAUSED + || call.status == lrc::api::call::Status::INCOMING_RINGING)) || call.isOutgoing)); } return QVariant(false); diff --git a/src/utils.h b/src/utils.h index 5f8095aa3..2cec141cf 100644 --- a/src/utils.h +++ b/src/utils.h @@ -45,7 +45,7 @@ #include <windows.h> #undef ERROR #else -#define LPCWSTR char* +#define LPCWSTR char * #endif #include "api/account.h" @@ -58,8 +58,8 @@ namespace Utils { /* * App/System */ -bool CreateStartupLink(const std::wstring& wstrAppName); -void DeleteStartupLink(const std::wstring& wstrAppName); +bool CreateStartupLink(const std::wstring &wstrAppName); +void DeleteStartupLink(const std::wstring &wstrAppName); bool CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink); bool CheckStartupLink(const std::wstring& wstrAppName); const char* WinGetEnv(const char* name); @@ -69,14 +69,14 @@ QString GetISODate(); void showSystemNotification(QWidget* widget, const QString& message, long delay = 5000, - const QString& triggeredAccountId = ""); -void showSystemNotification(QWidget* widget, - const QString& sender, - const QString& message, + const QString &triggeredAccountId = ""); +void showSystemNotification(QWidget *widget, + const QString &sender, + const QString &message, long delay = 5000, - const QString& triggeredAccountId = ""); -QSize getRealSize(QScreen* screen); -void forceDeleteAsync(const QString& path); + const QString &triggeredAccountId = ""); +QSize getRealSize(QScreen *screen); +void forceDeleteAsync(const QString &path); QString getChangeLog(); QString getProjectCredits(); void removeOldVersions(); @@ -90,8 +90,8 @@ static constexpr bool isBeta = true; static constexpr bool isBeta = false; #endif void cleanUpdateFiles(); -void checkForUpdates(bool withUI, QWidget* parent = nullptr); -void applyUpdates(bool updateToBeta, QWidget* parent = nullptr); +void checkForUpdates(bool withUI, QWidget *parent = nullptr); +void applyUpdates(bool updateToBeta, QWidget *parent = nullptr); /* * LRC helpers @@ -118,8 +118,8 @@ bool getReplyMessageBox(QWidget* widget, const QString& title, const QString& te static const QSize defaultAvatarSize {128, 128}; QString getContactImageString(const QString& accountId, const QString& uid); QImage getCirclePhoto(const QImage original, int sizePhoto); -QImage conversationPhoto(const QString& convUid, - const lrc::api::account::Info& accountInfo, +QImage conversationPhoto(const QString &convUid, + const lrc::api::account::Info &accountInfo, bool filtered = false); QColor getAvatarColor(const QString& canonicalUri); QImage fallbackAvatar(const QString& canonicalUriStr, diff --git a/src/utilsadapter.cpp b/src/utilsadapter.cpp index 993498f77..c6de39714 100644 --- a/src/utilsadapter.cpp +++ b/src/utilsadapter.cpp @@ -233,6 +233,32 @@ UtilsAdapter::hasVideoCall() return LRCInstance::hasVideoCall(); } +bool +UtilsAdapter::hasCall(const QString &accountId) +{ + auto activeCalls = LRCInstance::getActiveCalls(); + for (const auto &callId : activeCalls) { + auto &accountInfo = LRCInstance::accountModel().getAccountInfo(accountId); + if (accountInfo.callModel->hasCall(callId)) { + return true; + } + } + return false; +} + +const QString +UtilsAdapter::getCallConvForAccount(const QString &accountId) +{ + // TODO: Currently returning first call, establish priority according to state? + for (const auto &callId : LRCInstance::getActiveCalls()) { + auto &accountInfo = LRCInstance::accountModel().getAccountInfo(accountId); + if (accountInfo.callModel->hasCall(callId)) { + return LRCInstance::getConversationFromCallId(callId, accountId).uid; + } + } + return ""; +} + const QString UtilsAdapter::getCallId(const QString& accountId, const QString& convUid) { @@ -251,6 +277,14 @@ UtilsAdapter::getCallId(const QString& accountId, const QString& convUid) return call->id; } +int +UtilsAdapter::getCallStatus(const QString &callId) +{ + const auto callStatus = LRCInstance::getCallInfo( + callId, LRCInstance::getCurrAccId()); + return static_cast<int>(callStatus->status); +} + const QString UtilsAdapter::getCallStatusStr(int statusInt) { diff --git a/src/utilsadapter.h b/src/utilsadapter.h index 4210fcfb7..3306e5ccf 100644 --- a/src/utilsadapter.h +++ b/src/utilsadapter.h @@ -64,7 +64,10 @@ public: Q_INVOKABLE void startPreviewing(bool force); Q_INVOKABLE void stopPreviewing(); Q_INVOKABLE bool hasVideoCall(); + Q_INVOKABLE bool hasCall(const QString &accountId); + Q_INVOKABLE const QString getCallConvForAccount(const QString &accountId); Q_INVOKABLE const QString getCallId(const QString& accountId, const QString& convUid); + Q_INVOKABLE int getCallStatus(const QString &callId); Q_INVOKABLE const QString getCallStatusStr(int statusInt); Q_INVOKABLE QString getStringUTF8(QString string); Q_INVOKABLE bool validateRegNameForm(const QString& regName); -- GitLab