1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-09-05 14:43:39 +02:00

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
This commit is contained in:
ababi 2020-08-31 13:17:44 +02:00
parent 3e1f151f83
commit be70e36bf7
21 changed files with 617 additions and 551 deletions

View file

@ -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>

After

Width:  |  Height:  |  Size: 215 B

View file

@ -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>

After

Width:  |  Height:  |  Size: 256 B

View file

@ -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>

After

Width:  |  Height:  |  Size: 205 B

View file

@ -112,5 +112,6 @@
<file>src/wizardview/components/AccountCreationStepIndicator.qml</file> <file>src/wizardview/components/AccountCreationStepIndicator.qml</file>
<file>src/commoncomponents/SpinnerButton.qml</file> <file>src/commoncomponents/SpinnerButton.qml</file>
<file>src/commoncomponents/UsernameLineEdit.qml</file> <file>src/commoncomponents/UsernameLineEdit.qml</file>
<file>src/mainview/components/UserInfoCallPage.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -119,5 +119,6 @@
<file>images/icons/person_add-24px.svg</file> <file>images/icons/person_add-24px.svg</file>
<file>images/icons/router-24px.svg</file> <file>images/icons/router-24px.svg</file>
<file>images/icons/insert_drive_file-24dp.svg</file> <file>images/icons/insert_drive_file-24dp.svg</file>
<file>images/icons/arrow_back-white-24dp.svg</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -23,10 +23,10 @@
*/ */
#include "calladapter.h" #include "calladapter.h"
#include "globalsystemtray.h"
#include "utils.h" #include "utils.h"
#include <QApplication>
CallAdapter::CallAdapter(QObject* parent) CallAdapter::CallAdapter(QObject* parent)
: QmlAdapterBase(parent) : QmlAdapterBase(parent)
, oneSecondTimer_(new QTimer(this)) , oneSecondTimer_(new QTimer(this))
@ -94,7 +94,6 @@ CallAdapter::refuseACall(const QString& accountId, const QString& convUid)
void void
CallAdapter::acceptACall(const QString& accountId, const QString& convUid) CallAdapter::acceptACall(const QString& accountId, const QString& convUid)
{ {
emit incomingCallNeedToSetupMainView(accountId, convUid);
auto* convModel = LRCInstance::getCurrentConversationModel(); auto* convModel = LRCInstance::getCurrentConversationModel();
const auto convInfo = convModel->getConversationForUID(convUid); const auto convInfo = convModel->getConversationForUID(convUid);
if (!convInfo.uid.isEmpty()) { if (!convInfo.uid.isEmpty()) {
@ -107,50 +106,35 @@ CallAdapter::acceptACall(const QString& accountId, const QString& convUid)
void void
CallAdapter::slotShowIncomingCallView(const QString& accountId, const conversation::Info& convInfo) CallAdapter::slotShowIncomingCallView(const QString& accountId, const conversation::Info& convInfo)
{ {
auto selectedAccountId = LRCInstance::getCurrAccId();
auto* callModel = LRCInstance::getCurrentCallModel(); auto* callModel = LRCInstance::getCurrentCallModel();
if (!callModel->hasCall(convInfo.callId)) { 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_); if (QApplication::focusObject() == nullptr || accountId != selectedAccountId) {
showNotification(accountId, convInfo);
closeIncomingCallPageConnection_ return;
= 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);
} }
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; return;
} }
@ -163,15 +147,55 @@ CallAdapter::slotShowIncomingCallView(const QString& accountId, const conversati
emit showCallStack(accountId, convInfo.uid); emit showCallStack(accountId, convInfo.uid);
} }
} else { } else {
auto selectedAccountId = LRCInstance::getCurrentAccountInfo().id; auto showIncomingCall = false;
auto accountProperties = LRCInstance::accountModel().getAccountConfig(selectedAccountId); auto accountProperties = LRCInstance::accountModel().getAccountConfig(selectedAccountId);
if (!accountProperties.autoAnswer && !accountProperties.isRendezVous) { 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 showIncomingCallPage(accountId, convInfo.uid);
emit showCallStack(accountId, convInfo.uid, true);
} }
} }
emit callStatusChanged(static_cast<int>(call.status), accountId, convInfo.uid); emit callStatusChanged(static_cast<int>(call.status), accountId, convInfo.uid);
emit updateConversationSmartList(); emit updateConversationSmartList();
} }
@ -275,6 +299,48 @@ CallAdapter::getConferencesInfos()
return map; 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 void
CallAdapter::connectCallModel(const QString& accountId) CallAdapter::connectCallModel(const QString& accountId)
{ {
@ -385,7 +451,6 @@ CallAdapter::connectCallModel(const QString& accountId)
} }
} else { } else {
emit closeCallStack(accountId, convInfo.uid); emit closeCallStack(accountId, convInfo.uid);
emit closePotentialIncomingCallPageWindow(accountId, convInfo.uid);
} }
break; break;

View file

@ -21,10 +21,12 @@
#include "lrcinstance.h" #include "lrcinstance.h"
#include "qmladapterbase.h" #include "qmladapterbase.h"
#include "globalsystemtray.h"
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QVariant> #include <QVariant>
#include <QSystemTrayIcon>
class CallAdapter final : public QmlAdapterBase class CallAdapter final : public QmlAdapterBase
{ {
@ -70,14 +72,14 @@ signals:
void showVideoCallPage(const QString& accountId, const QString& convUid, const QString& callId); void showVideoCallPage(const QString& accountId, const QString& convUid, const QString& callId);
void showCallStack(const QString& accountId, const QString& convUid, bool forceReset = false); void showCallStack(const QString& accountId, const QString& convUid, bool forceReset = false);
void closeCallStack(const QString& accountId, const QString& convUid); 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 callStatusChanged(int index, const QString& accountId, const QString& convUid);
void updateConversationSmartList(); void updateConversationSmartList();
void updateParticipantsInfos(const QVariantList& infos, void updateParticipantsInfos(const QVariantList& infos,
const QString& accountId, const QString& accountId,
const QString& callId); 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); void previewVisibilityNeedToChange(bool visible);
/* /*
@ -105,6 +107,7 @@ private:
const QString& accountId = {}, const QString& accountId = {},
bool forceCallOnly = false); bool forceCallOnly = false);
bool shouldShowPreview(bool force); bool shouldShowPreview(bool force);
void showNotification(const QString& accountId, const lrc::api::conversation::Info& convInfo);
/* /*
* Current conf/call info. * Current conf/call info.
@ -115,6 +118,7 @@ private:
QMetaObject::Connection callStatusChangedConnection_; QMetaObject::Connection callStatusChangedConnection_;
QMetaObject::Connection onParticipantsChangedConnection_; QMetaObject::Connection onParticipantsChangedConnection_;
QMetaObject::Connection closeIncomingCallPageConnection_; QMetaObject::Connection closeIncomingCallPageConnection_;
QMetaObject::Connection appStateChangedConnection_;
/* /*
* For Call Overlay * For Call Overlay

View file

@ -33,13 +33,13 @@ GlobalSystemTray::getTriggeredAccountId()
} }
void void
GlobalSystemTray::setPossibleOnGoingConversationInfo(const lrc::api::conversation::Info& convInfo) GlobalSystemTray::setPossibleOnGoingConversationUid(const QString& convUid)
{ {
triggeredOnGoingConvInfo_ = convInfo; triggeredOnGoingConvUid_ = convUid;
} }
const lrc::api::conversation::Info& const QString&
GlobalSystemTray::getPossibleOnGoingConversationInfo() GlobalSystemTray::getPossibleOnGoingConversationUid()
{ {
return triggeredOnGoingConvInfo_; return triggeredOnGoingConvUid_;
} }

View file

@ -41,13 +41,13 @@ public:
const QString& getTriggeredAccountId(); 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: private:
GlobalSystemTray(); GlobalSystemTray();
QString triggeredAccountId_; QString triggeredAccountId_;
lrc::api::conversation::Info triggeredOnGoingConvInfo_; QString triggeredOnGoingConvUid_;
}; };

View file

@ -25,7 +25,6 @@ import net.jami.Models 1.0
import net.jami.Adapters 1.0 import net.jami.Adapters 1.0
// Import qml component files. // Import qml component files.
import "components" import "components"
import "../wizardview" import "../wizardview"
import "../settingsview" import "../settingsview"
@ -47,38 +46,76 @@ Window {
property int savedSidePanelViewMaxWidth: 0 property int savedSidePanelViewMaxWidth: 0
property int savedWelcomeViewMinWidth: 0 property int savedWelcomeViewMinWidth: 0
property int savedWelcomeViewMaxWidth: 0 property int savedWelcomeViewMaxWidth: 0
property bool sidePanelHidden: false property bool sidePanelHidden: !mainViewStack.visible
// To calculate tab bar bottom border hidden rect left margin. // To calculate tab bar bottom border hidden rect left margin.
property int tabBarLeftMargin: 8 property int tabBarLeftMargin: 8
property int tabButtonShrinkSize: 8 property int tabButtonShrinkSize: 8
property bool inSettingsView: false property bool inSettingsView: false
property bool needToShowCallStack: false
property bool needToCloseCallStack: false
signal closeApp signal closeApp
signal noAccountIsAvailable signal noAccountIsAvailable
function pushCallStackView(){ function showWelcomeView() {
if (mainViewStack.visible) { mainViewWindowSidePanel.deselectConversationSmartList()
mainViewStack.pop(null, StackView.Immediate) if (communicationPageMessageWebView.visible || callStackView.visible) {
mainViewStack.push(callStackView, StackView.Immediate) sidePanelViewStack.pop(StackView.Immediate)
} else { if (!sidePanelHidden) {
sidePanelViewStack.pop(null, StackView.Immediate) mainViewStack.pop(welcomePage, StackView.Immediate)
sidePanelViewStack.push(callStackView, StackView.Immediate) }
} }
} }
function pushCommunicationMessageWebView(){ function setCallStackView() {
if (mainViewStack.visible) {
mainViewStack.pop(null, StackView.Immediate) mainViewWindowSidePanel.deselectConversationSmartList()
mainViewStack.push(communicationPageMessageWebView,
StackView.Immediate) 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 { } else {
sidePanelViewStack.pop(null, StackView.Immediate) sidePanelViewStack.pop(StackView.Immediate)
sidePanelViewStack.push( mainViewStack.pop(null, StackView.Immediate)
communicationPageMessageWebView, mainViewStack.push(callStackView, StackView.Immediate)
StackView.Immediate) }
}
function pushCommunicationMessageWebView() {
if (sidePanelHidden) {
sidePanelViewStack.push(communicationPageMessageWebView, StackView.Immediate)
} else {
mainViewStack.pop(null, StackView.Immediate)
mainViewStack.push(communicationPageMessageWebView, StackView.Immediate)
} }
} }
@ -86,6 +123,10 @@ Window {
mainViewWindowSidePanel.refreshAccountComboBox(index) mainViewWindowSidePanel.refreshAccountComboBox(index)
} }
function currentAccountIsCalling() {
return UtilsAdapter.hasCall(AccountAdapter.currentAccountId)
}
function recursionStackViewItemMove(stackOne, stackTwo, depth=1) { function recursionStackViewItemMove(stackOne, stackTwo, depth=1) {
// Move all items (expect the bottom item) to stacktwo by the same order in stackone. // 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) mainViewStack.push(settingsView, StackView.Immediate)
sidePanelViewStack.push(leftPanelSettingsView, StackView.Immediate) sidePanelViewStack.push(leftPanelSettingsView, StackView.Immediate)
} }
ConversationsAdapter.disconnectConversationModel()
} else { } else {
ConversationsAdapter.connectConversationModel(false) if (sidePanelHidden) {
ConversationsAdapter.refill() // to be sure to have latest informations
mainViewWindowSidePanel.forceUpdateConversationSmartListView()
if (!sidePanelHidden) {
sidePanelViewStack.pop(mainViewWindowSidePanel, StackView.Immediate)
mainViewStack.pop(StackView.Immediate)
} else {
recursionStackViewItemMove(sidePanelViewStack, mainViewStack, 2) recursionStackViewItemMove(sidePanelViewStack, mainViewStack, 2)
sidePanelViewStack.pop(StackView.Immediate) sidePanelViewStack.pop(StackView.Immediate)
mainViewStack.pop(StackView.Immediate) mainViewStack.pop(StackView.Immediate)
recursionStackViewItemMove(mainViewStack, sidePanelViewStack, 1) recursionStackViewItemMove(mainViewStack, sidePanelViewStack, 1)
} }
if (needToCloseCallStack) { if (currentAccountIsCalling()) {
pushCommunicationMessageWebView() setCallStackView()
needToShowCallStack = false } else {
needToCloseCallStack = false mainViewWindowSidePanel.deselectConversationSmartList()
sidePanelViewStack.pop(StackView.Immediate)
if (!sidePanelHidden) {
mainViewStack.pop(welcomePage, StackView.Immediate)
}
} }
} }
inSettingsView = !inSettingsView inSettingsView = !inSettingsView
@ -150,13 +188,11 @@ Window {
function onShowCallStack(accountId, convUid, forceReset) { function onShowCallStack(accountId, convUid, forceReset) {
needToShowCallStack = true
if (forceReset) { if (forceReset) {
callStackView.responsibleAccountId = accountId callStackView.responsibleAccountId = accountId
callStackView.responsibleConvUid = convUid callStackView.responsibleConvUid = convUid
} }
// Check if it is coming from the current responsible call, // Check if it is coming from the current responsible call,
// and push views onto the correct stackview // and push views onto the correct stackview
if (callStackView.responsibleAccountId === accountId if (callStackView.responsibleAccountId === accountId
@ -166,7 +202,6 @@ Window {
} }
function onCloseCallStack(accountId, convUid) { function onCloseCallStack(accountId, convUid) {
// Check if call stack view is on any of the stackview. // Check if call stack view is on any of the stackview.
if (callStackView.responsibleAccountId === accountId if (callStackView.responsibleAccountId === accountId
&& callStackView.responsibleConvUid === convUid) { && callStackView.responsibleConvUid === convUid) {
@ -174,20 +209,16 @@ Window {
return item.objectName === "callStackViewObject" return item.objectName === "callStackViewObject"
}) || sidePanelViewStack.find(function (item, index) { }) || sidePanelViewStack.find(function (item, index) {
return item.objectName === "callStackViewObject" return item.objectName === "callStackViewObject"
}) || (inSettingsView && needToShowCallStack)) { })) {
callStackView.needToCloseInCallConversationAndPotentialWindow()
if (!inSettingsView) { if (!inSettingsView) {
callStackView.needToCloseInCallConversationAndPotentialWindow()
pushCommunicationMessageWebView() 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. // Set up the call stack view that is needed by call overlay.
if (!inSettingsView) { if (!inSettingsView) {
@ -205,16 +236,14 @@ Window {
communicationPageMessageWebView.headerUserUserNameLabelText = (name !== id) ? id : "" communicationPageMessageWebView.headerUserUserNameLabelText = (name !== id) ? id : ""
callStackView.needToCloseInCallConversationAndPotentialWindow() callStackView.needToCloseInCallConversationAndPotentialWindow()
callStackView.setLinkedWebview( callStackView.setLinkedWebview(communicationPageMessageWebView)
communicationPageMessageWebView)
callStackView.responsibleAccountId = accountId callStackView.responsibleAccountId = accountId
callStackView.responsibleConvUid = convUid callStackView.responsibleConvUid = convUid
callStackView.updateCorrspondingUI() callStackView.updateCorrespondingUI()
mainViewWindowSidePanel.refreshAccountComboBox(index) mainViewWindowSidePanel.refreshAccountComboBox(index)
ConversationsAdapter.selectConversation(accountId, convUid) ConversationsAdapter.selectConversation(accountId, convUid, !fromNotification)
MessagesAdapter.setupChatView(convUid) MessagesAdapter.setupChatView(convUid)
} }
} }
@ -264,7 +293,9 @@ Window {
Rectangle { Rectangle {
implicitWidth: 1 implicitWidth: 1
implicitHeight: splitView.height 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 id: mainViewSidePanelRect
SplitView.minimumWidth: sidePanelViewStackPreferredWidth SplitView.minimumWidth: sidePanelViewStackPreferredWidth
SplitView.maximumWidth: (sidePanelHidden ? splitView.width : SplitView.maximumWidth: (sidePanelHidden ? splitView.width :
splitView.width - sidePanelViewStackPreferredWidth) splitView.width - sidePanelViewStackPreferredWidth)
SplitView.fillHeight: true SplitView.fillHeight: true
// AccountComboBox is always visible // AccountComboBox is always visible
AccountComboBox { AccountComboBox {
id: accountComboBox id: accountComboBox
@ -304,24 +336,26 @@ Window {
} }
onAccountChanged: { onAccountChanged: {
mainViewWindowSidePanel.deselectConversationSmartList()
mainViewWindowSidePanel.refreshAccountComboBox(index) mainViewWindowSidePanel.refreshAccountComboBox(index)
settingsView.slotAccountListChanged() settingsView.slotAccountListChanged()
settingsView.setSelected(settingsView.selectedMenu, true) settingsView.setSelected(settingsView.selectedMenu, true)
if (needToShowCallStack if (!inSettingsView) {
&& callStackView.responsibleAccountId === UtilsAdapter.getCurrAccId()){ if (currentAccountIsCalling()) {
if (!AccountAdapter.hasVideoCall()) { setCallStackView()
pushCommunicationMessageWebView() } else {
needToShowCallStack = false showWelcomeView()
} else if (needToShowCallStack) {
pushCallStackView()
} }
} }
} }
onNeedToBackToWelcomePage: { onNeedToBackToWelcomePage: {
if (!inSettingsView) if (!inSettingsView && !currentAccountIsCalling()) {
mainViewWindowSidePanel.accountComboBoxNeedToShowWelcomePage() mainViewWindowSidePanel.accountComboBoxNeedToShowWelcomePage()
}
} }
onNewAccountButtonClicked: { onNewAccountButtonClicked: {
@ -338,7 +372,8 @@ Window {
initialItem: mainViewWindowSidePanel initialItem: mainViewWindowSidePanel
anchors.top: accountComboBox.visible ? accountComboBox.bottom : mainViewSidePanelRect.top anchors.top: accountComboBox.visible ? accountComboBox.bottom :
mainViewSidePanelRect.top
width: mainViewSidePanelRect.width width: mainViewSidePanelRect.width
height: accountComboBox.visible ? mainViewSidePanelRect.height - accountComboBox.height : height: accountComboBox.visible ? mainViewSidePanelRect.height - accountComboBox.height :
mainViewSidePanelRect.height mainViewSidePanelRect.height
@ -352,7 +387,8 @@ Window {
initialItem: welcomePage initialItem: welcomePage
SplitView.maximumWidth: sidePanelHidden ? splitView.width : splitView.width - sidePanelViewStackPreferredWidth SplitView.maximumWidth: sidePanelHidden ? splitView.width :
splitView.width - sidePanelViewStackPreferredWidth
SplitView.minimumWidth: sidePanelViewStackPreferredWidth SplitView.minimumWidth: sidePanelViewStackPreferredWidth
SplitView.fillHeight: true SplitView.fillHeight: true
@ -410,7 +446,6 @@ Window {
} }
} }
SidePanel { SidePanel {
id: mainViewWindowSidePanel id: mainViewWindowSidePanel
@ -422,11 +457,11 @@ Window {
callStackView.needToCloseInCallConversationAndPotentialWindow() callStackView.needToCloseInCallConversationAndPotentialWindow()
callStackView.responsibleAccountId = UtilsAdapter.getCurrAccId() callStackView.responsibleAccountId = UtilsAdapter.getCurrAccId()
callStackView.responsibleConvUid = currentUID callStackView.responsibleConvUid = currentUID
callStackView.updateCorrspondingUI() callStackView.updateCorrespondingUI()
if (callStackViewShouldShow) { if (callStackViewShouldShow) {
if (callState === Call.Status.IN_PROGRESS || callState === Call.Status.PAUSED) { if (callState === Call.Status.IN_PROGRESS || callState === Call.Status.PAUSED) {
UtilsAdapter.setCurrentCall(UtilsAdapter.getCurrAccId(), currentUID) UtilsAdapter.setCurrentCall(AccountAdapter.currentAccountId, currentUID)
if (isAudioOnly) if (isAudioOnly)
callStackView.showAudioCallPage() callStackView.showAudioCallPage()
else else
@ -434,15 +469,17 @@ Window {
UtilsAdapter.getCallId( UtilsAdapter.getCallId(
callStackView.responsibleAccountId, callStackView.responsibleAccountId,
callStackView.responsibleConvUid)) callStackView.responsibleConvUid))
} else if (callState === Call.Status.INCOMING_RINGING) {
callStackView.showIncomingCallPage(AccountAdapter.currentAccountId,
currentUID)
} else { } else {
callStackView.showOutgoingCallPage(callStateStr) callStackView.showOutgoingCallPage()
} }
} }
// Set up chatview. // Set up chatview.
MessagesAdapter.setupChatView(currentUID) MessagesAdapter.setupChatView(currentUID)
callStackView.setLinkedWebview( callStackView.setLinkedWebview(communicationPageMessageWebView)
communicationPageMessageWebView)
if (mainViewStack.find(function (item, index) { if (mainViewStack.find(function (item, index) {
return item.objectName === "communicationPageMessageWebView" return item.objectName === "communicationPageMessageWebView"
@ -453,7 +490,6 @@ Window {
return return
} }
// Push messageWebView or callStackView onto the correct stackview // Push messageWebView or callStackView onto the correct stackview
mainViewStack.pop(null, StackView.Immediate) mainViewStack.pop(null, StackView.Immediate)
sidePanelViewStack.pop(null, StackView.Immediate) sidePanelViewStack.pop(null, StackView.Immediate)
@ -483,13 +519,13 @@ Window {
onAccountComboBoxNeedToShowWelcomePage: { onAccountComboBoxNeedToShowWelcomePage: {
// If the item argument is specified, all items down to (but not including) item will be popped. // If the item argument is specified, all items down to (but not including) item will be popped.
if (!inSettingsView) { if (!inSettingsView && !currentAccountIsCalling()) {
mainViewStack.pop(welcomePage) showWelcomeView()
} }
} }
onConversationSmartListViewNeedToShowWelcomePage: { onConversationSmartListViewNeedToShowWelcomePage: {
mainViewStack.pop(welcomePage) showWelcomeView()
} }
onNeedToUpdateConversationForAddedContact: { onNeedToUpdateConversationForAddedContact: {
@ -559,22 +595,15 @@ Window {
} }
onNeedToGoBackToWelcomeView: { onNeedToGoBackToWelcomeView: {
mainViewWindowSidePanel.deselectConversationSmartList() showWelcomeView()
if (communicationPageMessageWebView.visible
&& !mainViewStack.visible) {
sidePanelViewStack.pop()
} else if (communicationPageMessageWebView.visible
&& mainViewStack.visible) {
mainViewStack.pop()
}
recordBox.visible = false recordBox.visible = false
} }
Component.onCompleted: { Component.onCompleted: {
sidePanelViewStack.SplitView.maximumWidth = Qt.binding(function() { sidePanelViewStack.SplitView.maximumWidth = Qt.binding(function() {
return (hiddenView ? splitView.width : return (sidePanelHidden ? splitView.width :
splitView.width - sidePanelViewStackPreferedWidth) splitView.width - sidePanelViewStackPreferredWidth)
}) })
recordBox.x = Qt.binding(function() { recordBox.x = Qt.binding(function() {
@ -592,7 +621,6 @@ Window {
sidePanelViewStack.height + recordBox.y_offset sidePanelViewStack.height + recordBox.y_offset
}) })
// Set qml MessageWebView object pointer to c++. // Set qml MessageWebView object pointer to c++.
MessagesAdapter.setQmlObject(this) MessagesAdapter.setQmlObject(this)
} }
@ -604,7 +632,6 @@ Window {
+ mainViewStackPreferredWidth - 5 + mainViewStackPreferredWidth - 5
&& mainViewStack.visible) { && mainViewStack.visible) {
mainViewStack.visible = false mainViewStack.visible = false
sidePanelHidden = true
// The find callback function is called for each item in the stack. // The find callback function is called for each item in the stack.
var inWelcomeViewStack = mainViewStack.find( var inWelcomeViewStack = mainViewStack.find(
@ -621,7 +648,6 @@ Window {
+ mainViewStackPreferredWidth + 5 + mainViewStackPreferredWidth + 5
&& !mainViewStack.visible) { && !mainViewStack.visible) {
mainViewStack.visible = true mainViewStack.visible = true
sidePanelHidden = false
var inSidePanelViewStack = sidePanelViewStack.find( var inSidePanelViewStack = sidePanelViewStack.find(
function (item, index) { function (item, index) {
@ -631,6 +657,9 @@ Window {
recursionStackViewItemMove(sidePanelViewStack, mainViewStack, (inSettingsView ? 2 : 1)) recursionStackViewItemMove(sidePanelViewStack, mainViewStack, (inSettingsView ? 2 : 1))
} }
if (!inSettingsView && currentAccountIsCalling())
pushCallStackView()
mainViewWindow.update() mainViewWindow.update()
} }
} }

View file

@ -151,6 +151,31 @@ Rectangle {
anchors.fill: parent 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 { Text {
id: jamiBestNameText id: jamiBestNameText

View file

@ -32,6 +32,12 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
Shortcut {
sequence: "Ctrl+D"
context: Qt.ApplicationShortcut
onActivated: CallAdapter.hangUpThisCall()
}
// When selected conversation is changed, // When selected conversation is changed,
// these values will also be changed. // these values will also be changed.
property string responsibleConvUid: "" property string responsibleConvUid: ""
@ -41,7 +47,6 @@ Rectangle {
audioCallPage.closeInCallConversation() audioCallPage.closeInCallConversation()
videoCallPage.closeInCallConversation() videoCallPage.closeInCallConversation()
// Close potential window, context menu releated windows. // Close potential window, context menu releated windows.
audioCallPage.closeContextMenuAndRelatedWindows() audioCallPage.closeContextMenuAndRelatedWindows()
@ -54,9 +59,10 @@ Rectangle {
videoCallPage.setLinkedWebview(webViewId) videoCallPage.setLinkedWebview(webViewId)
} }
function updateCorrspondingUI() { function updateCorrespondingUI() {
audioCallPage.updateUI(responsibleAccountId, responsibleConvUid) audioCallPage.updateUI(responsibleAccountId, responsibleConvUid)
outgoingCallPage.updateUI(responsibleAccountId, responsibleConvUid) outgoingCallPage.updateUI(responsibleAccountId, responsibleConvUid)
incomingCallPage.updateUI(responsibleAccountId, responsibleConvUid)
videoCallPage.updateUI(responsibleAccountId, responsibleConvUid) videoCallPage.updateUI(responsibleAccountId, responsibleConvUid)
} }
@ -73,7 +79,7 @@ Rectangle {
audioCallPage.updateUI(responsibleAccountId, responsibleConvUid) audioCallPage.updateUI(responsibleAccountId, responsibleConvUid)
} }
function showOutgoingCallPage(currentCallStatus) { function showOutgoingCallPage() {
var itemToFind = callStackMainView.find(function (item) { var itemToFind = callStackMainView.find(function (item) {
return item.stackNumber === 1 return item.stackNumber === 1
}) })
@ -83,8 +89,21 @@ Rectangle {
} else { } else {
callStackMainView.pop(itemToFind, StackView.Immediate) 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) { function showVideoCallPage(callId) {
@ -106,7 +125,6 @@ Rectangle {
function onShowOutgoingCallPage(accountId, convUid) { function onShowOutgoingCallPage(accountId, convUid) {
// Need to check whether it is the current selected conversation. // Need to check whether it is the current selected conversation.
if (responsibleConvUid === convUid if (responsibleConvUid === convUid
&& responsibleAccountId === accountId) { && responsibleAccountId === accountId) {
@ -115,18 +133,7 @@ Rectangle {
} }
function onShowIncomingCallPage(accountId, convUid) { function onShowIncomingCallPage(accountId, convUid) {
showIncomingCallPage(accountId, convUid)
// Check is done within the js.
IncomingCallPageCreation.createincomingCallPageWindowObjects(
accountId, convUid)
IncomingCallPageCreation.showIncomingCallPageWindow(accountId,
convUid)
}
function onClosePotentialIncomingCallPageWindow(accountId, convUid) {
IncomingCallPageCreation.closeIncomingCallPageWindow(accountId,
convUid)
} }
function onShowAudioCallPage(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 { StackView {
id: callStackMainView id: callStackMainView

View file

@ -25,313 +25,128 @@ import net.jami.Models 1.0
import "../../commoncomponents" import "../../commoncomponents"
Rectangle {
// IncomingCallPage as a seperate window,
// exist at the right bottom, as a notification to user that
// a call is incoming.
Window {
id: incomingCallPage id: incomingCallPage
property int minWidth: 300 property int buttonPreferredSize: 48
property int minHeight: 400
signal callCancelButtonIsClicked
signal callAcceptButtonIsClicked
// The unique identifier for incomingCallPage color: "black"
property string responsibleAccountId: ""
property string responsibleConvUid: ""
property string contactImgSource: "" function updateUI(accountId, convUid) {
property string bestName: "Best Name" userInfoIncomingCallPage.updateUI(accountId, convUid)
property string bestId: "Best Id"
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 updatePositionToRightBottom() { // Prevent right click propagate to VideoCallPage.
MouseArea {
anchors.fill: parent
// Screen right bottom, propagateComposedEvents: false
// since the qt screen.virtualY, virtualX does not work properly, acceptedButtons: Qt.RightButton
// 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
} }
minimumWidth: minWidth ColumnLayout {
minimumHeight: minHeight id: incomingCallPageColumnLayout
maximumWidth: minWidth + 300
maximumHeight: minHeight + 300
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
screen: Qt.application.screens[0]
Rectangle {
id: incomingCallPageColumnLayoutMainRect
anchors.fill: parent anchors.fill: parent
radius: 15 // Common elements with OutgoingCallPage
color: "black" UserInfoCallPage {
id: userInfoIncomingCallPage
Layout.fillWidth: true
// Simulate window drag. (top with height 30). Layout.fillHeight: true
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
}
} }
HoverableButton { Text {
id: closeButton id: talkToYouText
anchors.top: incomingCallPageColumnLayoutMainRect.top Layout.alignment: Qt.AlignCenter
anchors.topMargin: 10 Layout.preferredWidth: incomingCallPage.width
anchors.right: incomingCallPageColumnLayoutMainRect.right Layout.preferredHeight: 32
anchors.rightMargin: 10
width: 30 font.pointSize: JamiTheme.textFontSize
height: 30
radius: 30 horizontalAlignment: Text.AlignHCenter
source: "qrc:/images/icons/ic_close_white_24dp.png" verticalAlignment: Text.AlignVCenter
backgroundColor: "black" color: "white"
onEnterColor: JamiTheme.closeButtonLighterBlack
onExitColor: "black"
onPressColor: JamiTheme.declineButtonPressedRed
onReleaseColor: "black"
onClicked: { text: "is calling you"
incomingCallPage.close()
CallAdapter.refuseACall(responsibleAccountId,
responsibleConvUid)
}
} }
ColumnLayout { RowLayout {
id: incomingCallPageColumnLayout id: incomingCallPageRowLayout
anchors.fill: parent Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
Layout.bottomMargin: 48
Layout.topMargin: 48
Image { Layout.preferredWidth: incomingCallPage.width - 200
id: contactImg Layout.maximumWidth: 200
Layout.preferredHeight: buttonPreferredSize
Layout.alignment: Qt.AlignCenter ColumnLayout {
Layout.topMargin: 30 id: callAnswerButtonColumnLayout
Layout.preferredWidth: 100 Layout.alignment: Qt.AlignLeft
Layout.preferredHeight: 100
fillMode: Image.PreserveAspectFit HoverableButton {
source: contactImgSource id: callAnswerButton
}
Rectangle { Layout.alignment: Qt.AlignCenter
id: incomingCallPageTextRect
Layout.alignment: Qt.AlignCenter Layout.preferredWidth: buttonPreferredSize
Layout.topMargin: 5 Layout.preferredHeight: buttonPreferredSize
Layout.preferredWidth: incomingCallPage.width backgroundColor: JamiTheme.acceptButtonGreen
Layout.preferredHeight: jamiBestNameText.height + jamiBestIdText.height onEnterColor: JamiTheme.acceptButtonHoverGreen
+ talkToYouText.height + 20 onPressColor: JamiTheme.acceptButtonPressedGreen
onReleaseColor: JamiTheme.acceptButtonHoverGreen
onExitColor: JamiTheme.acceptButtonGreen
ColumnLayout { buttonImageHeight: buttonPreferredSize / 2
id: incomingCallPageTextRectColumnLayout buttonImageWidth: buttonPreferredSize / 2
source: "qrc:/images/icons/ic_check_white_18dp_2x.png"
radius: 32
Text { onClicked: {
id: jamiBestNameText callAcceptButtonIsClicked()
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: incomingCallPageTextRect.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: incomingCallPageTextRect.width - 30
elide: Qt.ElideMiddle
}
}
Text {
id: jamiBestIdText
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: incomingCallPageTextRect.width
Layout.preferredHeight: 30
font.pointSize: JamiTheme.textFontSize
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: textMetricsjamiBestIdText.elidedText
color: "white"
TextMetrics {
id: textMetricsjamiBestIdText
font: jamiBestIdText.font
text: bestId
elideWidth: incomingCallPageTextRect.width - 30
elide: Qt.ElideMiddle
}
}
Text {
id: talkToYouText
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: incomingCallPageTextRect.width
Layout.preferredHeight: 30
font.pointSize: JamiTheme.textFontSize
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: "white"
text: "is calling you"
} }
} }
color: "transparent"
} }
RowLayout { ColumnLayout {
id: incomingCallPageRowLayout id: callDeclineButtonColumnLayout
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom Layout.alignment: Qt.AlignRight
Layout.bottomMargin: 5
Layout.preferredWidth: incomingCallPage.width - 100 HoverableButton {
Layout.preferredHeight: buttonPreferredSize id: callDeclineButton
ColumnLayout { Layout.alignment: Qt.AlignCenter
id: callAnswerButtonColumnLayout
Layout.alignment: Qt.AlignLeft Layout.preferredWidth: buttonPreferredSize
Layout.preferredHeight: buttonPreferredSize
HoverableButton { backgroundColor: JamiTheme.declineButtonRed
id: callAnswerButton onEnterColor: JamiTheme.declineButtonHoverRed
onPressColor: JamiTheme.declineButtonPressedRed
onReleaseColor: JamiTheme.declineButtonHoverRed
onExitColor: JamiTheme.declineButtonRed
Layout.alignment: Qt.AlignCenter buttonImageHeight: buttonPreferredSize / 2
buttonImageWidth: buttonPreferredSize / 2
source: "qrc:/images/icons/ic_close_white_24dp.png"
radius: 32
Layout.preferredWidth: buttonPreferredSize onClicked: {
Layout.preferredHeight: buttonPreferredSize callCancelButtonIsClicked()
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")
} }
} }
} }
} }
} }
color: "transparent"
Shortcut { Shortcut {
sequence: "Ctrl+Y" sequence: "Ctrl+Y"
context: Qt.ApplicationShortcut context: Qt.ApplicationShortcut

View file

@ -30,25 +30,16 @@ Rectangle {
property int buttonPreferredSize: 50 property int buttonPreferredSize: 50
property int callStatus: 0 property int callStatus: 0
property string contactImgSource: ""
property string bestName: "Best Name"
property string bestId: "Best Id"
signal callCancelButtonIsClicked signal callCancelButtonIsClicked
function updateUI(accountId, convUid) { function updateUI(accountId, convUid) {
contactImgSource = "data:image/png;base64," + UtilsAdapter.getContactImageString( userInfoCallPage.updateUI(accountId, convUid)
accountId, convUid)
bestName = UtilsAdapter.getBestName(accountId, convUid)
var id = UtilsAdapter.getBestId(accountId, convUid)
bestId = (bestName !== id) ? id : ""
} }
anchors.fill: parent anchors.fill: parent
color: "black" color: "black"
// Prevent right click propagate to VideoCallPage. // Prevent right click propagate to VideoCallPage.
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@ -61,114 +52,43 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
Image { UserInfoCallPage {
id: contactImg id: userInfoCallPage
Layout.fillHeight: true
Layout.alignment: Qt.AlignCenter Layout.fillWidth: true
Layout.preferredWidth: 100
Layout.preferredHeight: 100
fillMode: Image.PreserveAspectFit
source: contactImgSource
asynchronous: true
} }
Rectangle { AnimatedImage {
id: outgoingCallPageTextRect id: spinnerImage
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
Layout.topMargin: 5 Layout.preferredWidth: 24
Layout.preferredHeight: 8
source: "qrc:/images/waiting.gif"
}
Text {
id: callStatusText
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: outgoingCallPageRect.width Layout.preferredWidth: outgoingCallPageRect.width
Layout.preferredHeight: jamiBestNameText.height + jamiBestIdText.height Layout.preferredHeight: 30
+ callStatusText.height + spinnerImage.height + 20
color: "transparent" font.pointSize: JamiTheme.textFontSize
ColumnLayout { horizontalAlignment: Text.AlignHCenter
id: outgoingCallPageTextRectColumnLayout verticalAlignment: Text.AlignVCenter
Text { text: UtilsAdapter.getCallStatusStr(callStatus) + "..."
id: jamiBestNameText color: Qt.lighter("white", 1.5)
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
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: textMetricsjamiBestIdText.elidedText
color: Qt.lighter("white", 1.5)
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)
}
}
} }
ColumnLayout { ColumnLayout {
id: callCancelButtonColumnLayout id: callCancelButtonColumnLayout
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
Layout.bottomMargin: 48
HoverableButton { HoverableButton {
id: callCancelButton id: callCancelButton
@ -192,18 +112,9 @@ Rectangle {
toolTipText: qsTr("Cancel the call") toolTipText: qsTr("Cancel the call")
onClicked: { onClicked: {
outgoingCallPageRect.callCancelButtonIsClicked() callCancelButtonIsClicked()
} }
} }
Text {
id: cancelText
Layout.alignment: Qt.AlignCenter
font.pointSize: JamiTheme.textFontSize - 2
text: qsTr("Cancel")
}
} }
} }
} }

View file

@ -32,7 +32,7 @@ Rectangle {
property int pendingRequestCount: 0 property int pendingRequestCount: 0
property int totalUnreadMessagesCount: 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 accountComboBoxNeedToShowWelcomePage()
signal conversationSmartListViewNeedToShowWelcomePage signal conversationSmartListViewNeedToShowWelcomePage
signal needToUpdateConversationForAddedContact signal needToUpdateConversationForAddedContact
@ -188,8 +188,7 @@ Rectangle {
target: ConversationsAdapter target: ConversationsAdapter
function onShowChatView(accountId, convUid) { function onShowChatView(accountId, convUid) {
conversationSmartListView.needToShowChatView(accountId, conversationSmartListView.needToShowChatView(accountId, convUid)
convUid)
} }
function onShowConversationTabs(visible) { function onShowConversationTabs(visible) {

View file

@ -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
}
}
}
}
}
}

View file

@ -112,7 +112,7 @@ Rectangle {
Connections { Connections {
id: accountConnections_ContactModel id: accountConnections_ContactModel
target: AccountAdapter.contactModel target: AccountAdapter.contactModel
enabled: accountViewRect.visible enabled: root.visible
function onModelUpdated(uri, needsSorted) { function onModelUpdated(uri, needsSorted) {
updateAndShowBannedContactsSlot() updateAndShowBannedContactsSlot()
@ -130,7 +130,7 @@ Rectangle {
Connections { Connections {
id: accountConnections_DeviceModel id: accountConnections_DeviceModel
target: AccountAdapter.deviceModel target: AccountAdapter.deviceModel
enabled: accountViewRect.visible enabled: root.visible
function onDeviceAdded(id) { function onDeviceAdded(id) {
updateAndShowDevicesSlot() updateAndShowDevicesSlot()

View file

@ -335,7 +335,8 @@ SmartListModel::getConversationItemData(const conversation::Info& item,
return QVariant(callModel->hasCall(convInfo.callId) return QVariant(callModel->hasCall(convInfo.callId)
&& ((!call.isOutgoing && ((!call.isOutgoing
&& (call.status == lrc::api::call::Status::IN_PROGRESS && (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)); || call.isOutgoing));
} }
return QVariant(false); return QVariant(false);

View file

@ -45,7 +45,7 @@
#include <windows.h> #include <windows.h>
#undef ERROR #undef ERROR
#else #else
#define LPCWSTR char* #define LPCWSTR char *
#endif #endif
#include "api/account.h" #include "api/account.h"
@ -58,8 +58,8 @@ namespace Utils {
/* /*
* App/System * App/System
*/ */
bool CreateStartupLink(const std::wstring& wstrAppName); bool CreateStartupLink(const std::wstring &wstrAppName);
void DeleteStartupLink(const std::wstring& wstrAppName); void DeleteStartupLink(const std::wstring &wstrAppName);
bool CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink); bool CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink);
bool CheckStartupLink(const std::wstring& wstrAppName); bool CheckStartupLink(const std::wstring& wstrAppName);
const char* WinGetEnv(const char* name); const char* WinGetEnv(const char* name);
@ -69,14 +69,14 @@ QString GetISODate();
void showSystemNotification(QWidget* widget, void showSystemNotification(QWidget* widget,
const QString& message, const QString& message,
long delay = 5000, long delay = 5000,
const QString& triggeredAccountId = ""); const QString &triggeredAccountId = "");
void showSystemNotification(QWidget* widget, void showSystemNotification(QWidget *widget,
const QString& sender, const QString &sender,
const QString& message, const QString &message,
long delay = 5000, long delay = 5000,
const QString& triggeredAccountId = ""); const QString &triggeredAccountId = "");
QSize getRealSize(QScreen* screen); QSize getRealSize(QScreen *screen);
void forceDeleteAsync(const QString& path); void forceDeleteAsync(const QString &path);
QString getChangeLog(); QString getChangeLog();
QString getProjectCredits(); QString getProjectCredits();
void removeOldVersions(); void removeOldVersions();
@ -90,8 +90,8 @@ static constexpr bool isBeta = true;
static constexpr bool isBeta = false; static constexpr bool isBeta = false;
#endif #endif
void cleanUpdateFiles(); void cleanUpdateFiles();
void checkForUpdates(bool withUI, QWidget* parent = nullptr); void checkForUpdates(bool withUI, QWidget *parent = nullptr);
void applyUpdates(bool updateToBeta, QWidget* parent = nullptr); void applyUpdates(bool updateToBeta, QWidget *parent = nullptr);
/* /*
* LRC helpers * LRC helpers
@ -118,8 +118,8 @@ bool getReplyMessageBox(QWidget* widget, const QString& title, const QString& te
static const QSize defaultAvatarSize {128, 128}; static const QSize defaultAvatarSize {128, 128};
QString getContactImageString(const QString& accountId, const QString& uid); QString getContactImageString(const QString& accountId, const QString& uid);
QImage getCirclePhoto(const QImage original, int sizePhoto); QImage getCirclePhoto(const QImage original, int sizePhoto);
QImage conversationPhoto(const QString& convUid, QImage conversationPhoto(const QString &convUid,
const lrc::api::account::Info& accountInfo, const lrc::api::account::Info &accountInfo,
bool filtered = false); bool filtered = false);
QColor getAvatarColor(const QString& canonicalUri); QColor getAvatarColor(const QString& canonicalUri);
QImage fallbackAvatar(const QString& canonicalUriStr, QImage fallbackAvatar(const QString& canonicalUriStr,

View file

@ -233,6 +233,32 @@ UtilsAdapter::hasVideoCall()
return LRCInstance::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 const QString
UtilsAdapter::getCallId(const QString& accountId, const QString& convUid) UtilsAdapter::getCallId(const QString& accountId, const QString& convUid)
{ {
@ -251,6 +277,14 @@ UtilsAdapter::getCallId(const QString& accountId, const QString& convUid)
return call->id; 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 const QString
UtilsAdapter::getCallStatusStr(int statusInt) UtilsAdapter::getCallStatusStr(int statusInt)
{ {

View file

@ -64,7 +64,10 @@ public:
Q_INVOKABLE void startPreviewing(bool force); Q_INVOKABLE void startPreviewing(bool force);
Q_INVOKABLE void stopPreviewing(); Q_INVOKABLE void stopPreviewing();
Q_INVOKABLE bool hasVideoCall(); 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 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 const QString getCallStatusStr(int statusInt);
Q_INVOKABLE QString getStringUTF8(QString string); Q_INVOKABLE QString getStringUTF8(QString string);
Q_INVOKABLE bool validateRegNameForm(const QString& regName); Q_INVOKABLE bool validateRegNameForm(const QString& regName);