mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-08-08 00:35:50 +02:00
swarm: add call buttons and interactions for multi-swarm
+ Add call buttons to start a new call + React to events from the swarm + call interactions (Join call/Call ended, etc) + active calls area + Add call management logic in LRC + Feature is enabled via the experimental checkbox https://git.jami.net/savoirfairelinux/jami-daemon/-/issues/312 Change-Id: I83fd20b5e772097c0792bdc66feec69b0cb0009a
This commit is contained in:
parent
9b2dbb64ea
commit
0996b167d9
43 changed files with 1276 additions and 90 deletions
2
daemon
2
daemon
|
@ -1 +1 @@
|
||||||
Subproject commit 54ffd0f4380bdbdc6a2fcb80847b2f5aefad4958
|
Subproject commit 08ef8dd80d571816259b195b0956a472a40b58ad
|
|
@ -54,6 +54,7 @@ extern const QString defaultDownloadPath;
|
||||||
X(NeverShowMeAgain, false) \
|
X(NeverShowMeAgain, false) \
|
||||||
X(WindowGeometry, QRectF(qQNaN(), qQNaN(), 0., 0.)) \
|
X(WindowGeometry, QRectF(qQNaN(), qQNaN(), 0., 0.)) \
|
||||||
X(WindowState, QWindow::AutomaticVisibility) \
|
X(WindowState, QWindow::AutomaticVisibility) \
|
||||||
|
X(EnableExperimentalSwarm, false) \
|
||||||
X(LANG, "SYSTEM")
|
X(LANG, "SYSTEM")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -216,6 +216,7 @@ CallAdapter::onParticipantUpdated(const QString& callId, int index)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto infos = getConferencesInfos();
|
auto infos = getConferencesInfos();
|
||||||
|
if (index < infos.size())
|
||||||
participantsModel_->updateParticipant(index, infos[index]);
|
participantsModel_->updateParticipant(index, infos[index]);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
}
|
}
|
||||||
|
@ -256,7 +257,8 @@ CallAdapter::onCallStatusChanged(const QString& callId, int code)
|
||||||
const auto& currentConvInfo = lrcInstance_->getConversationFromConvUid(currentConvId);
|
const auto& currentConvInfo = lrcInstance_->getConversationFromConvUid(currentConvId);
|
||||||
|
|
||||||
// was it a conference and now is a dialog?
|
// was it a conference and now is a dialog?
|
||||||
if (currentConvInfo.confId.isEmpty() && currentConfSubcalls_.size() == 2) {
|
if (currentConvInfo.isCoreDialog() && currentConvInfo.confId.isEmpty()
|
||||||
|
&& currentConfSubcalls_.size() == 2) {
|
||||||
auto it = std::find_if(currentConfSubcalls_.cbegin(),
|
auto it = std::find_if(currentConfSubcalls_.cbegin(),
|
||||||
currentConfSubcalls_.cend(),
|
currentConfSubcalls_.cend(),
|
||||||
[&callId](const QString& cid) { return cid != callId; });
|
[&callId](const QString& cid) { return cid != callId; });
|
||||||
|
@ -495,14 +497,12 @@ CallAdapter::updateCall(const QString& convUid, const QString& accountId, bool f
|
||||||
accountId_ = accountId.isEmpty() ? accountId_ : accountId;
|
accountId_ = accountId.isEmpty() ? accountId_ : accountId;
|
||||||
|
|
||||||
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid);
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid);
|
||||||
if (convInfo.uid.isEmpty()) {
|
if (convInfo.uid.isEmpty())
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
auto call = lrcInstance_->getCallInfoForConversation(convInfo, forceCallOnly);
|
auto call = lrcInstance_->getCallInfoForConversation(convInfo, forceCallOnly);
|
||||||
if (!call) {
|
if (!call)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (convInfo.uid == lrcInstance_->get_selectedConvUid()) {
|
if (convInfo.uid == lrcInstance_->get_selectedConvUid()) {
|
||||||
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
|
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
|
||||||
|
|
118
src/app/commoncomponents/CallMessageDelegate.qml
Normal file
118
src/app/commoncomponents/CallMessageDelegate.qml
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Savoir-faire Linux Inc.
|
||||||
|
* Author: Sébastien Blin <sebastien.blin@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
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import net.jami.Models 1.1
|
||||||
|
import net.jami.Adapters 1.1
|
||||||
|
import net.jami.Constants 1.1
|
||||||
|
|
||||||
|
SBSMessageBase {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
component JoinCallButton: PushButton {
|
||||||
|
visible: root.isActive
|
||||||
|
toolTipText: JamiStrings.joinCall
|
||||||
|
preferredSize: 40
|
||||||
|
imageColor: callLabel.color
|
||||||
|
normalColor: "transparent"
|
||||||
|
hoveredColor: Qt.rgba(255, 255, 255, 0.2)
|
||||||
|
border.width: 1
|
||||||
|
border.color: callLabel.color
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool isRemoteImage
|
||||||
|
|
||||||
|
isOutgoing: Author === ""
|
||||||
|
author: Author
|
||||||
|
readers: Readers
|
||||||
|
formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
|
||||||
|
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: CurrentConversation
|
||||||
|
enabled: root.isActive
|
||||||
|
|
||||||
|
function onActiveCallsChanged() {
|
||||||
|
root.isActive = LRCInstance.indexOfActiveCall(ConfId, ActionUri, DeviceId) !== -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool isActive: LRCInstance.indexOfActiveCall(ConfId, ActionUri, DeviceId) !== -1
|
||||||
|
visible: isActive || ConfId === "" || Duration > 0
|
||||||
|
|
||||||
|
bubble.color: {
|
||||||
|
if (ConfId === "" && Duration === 0) {
|
||||||
|
// If missed, we can add a darker pattern
|
||||||
|
return isOutgoing ?
|
||||||
|
Qt.darker(JamiTheme.messageOutBgColor, 1.5) :
|
||||||
|
Qt.darker(JamiTheme.messageInBgColor, 1.5)
|
||||||
|
}
|
||||||
|
return isOutgoing ?
|
||||||
|
JamiTheme.messageOutBgColor :
|
||||||
|
CurrentConversation.isCoreDialog ? JamiTheme.messageInBgColor : Qt.lighter(CurrentConversation.color, 1.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
innerContent.children: [
|
||||||
|
RowLayout {
|
||||||
|
id: msg
|
||||||
|
anchors.right: isOutgoing ? parent.right : undefined
|
||||||
|
spacing: 10
|
||||||
|
visible: root.visible
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: callLabel
|
||||||
|
padding: 10
|
||||||
|
Layout.margins: 8
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text:{
|
||||||
|
if (root.isActive)
|
||||||
|
return JamiStrings.joinCall
|
||||||
|
return Body
|
||||||
|
}
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
font.pointSize: JamiTheme.contactEventPointSize
|
||||||
|
font.bold: true
|
||||||
|
color: UtilsAdapter.luma(bubble.color) ?
|
||||||
|
JamiTheme.chatviewTextColorLight :
|
||||||
|
JamiTheme.chatviewTextColorDark
|
||||||
|
}
|
||||||
|
|
||||||
|
JoinCallButton {
|
||||||
|
id: joinCallInAudio
|
||||||
|
|
||||||
|
source: JamiResources.place_audiocall_24dp_svg
|
||||||
|
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, ConfId, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
JoinCallButton {
|
||||||
|
id: joinCallInVideo
|
||||||
|
|
||||||
|
source: JamiResources.videocam_24dp_svg
|
||||||
|
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, ConfId)
|
||||||
|
Layout.rightMargin: parent.spacing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
opacity: 0
|
||||||
|
Behavior on opacity { NumberAnimation { duration: 100 } }
|
||||||
|
Component.onCompleted: opacity = 1
|
||||||
|
}
|
|
@ -56,6 +56,7 @@ Control {
|
||||||
readonly property real hPadding: JamiTheme.sbsMessageBasePreferredPadding
|
readonly property real hPadding: JamiTheme.sbsMessageBasePreferredPadding
|
||||||
width: ListView.view ? ListView.view.width : 0
|
width: ListView.view ? ListView.view.width : 0
|
||||||
height: mainColumnLayout.implicitHeight
|
height: mainColumnLayout.implicitHeight
|
||||||
|
|
||||||
rightPadding: hPadding
|
rightPadding: hPadding
|
||||||
leftPadding: hPadding
|
leftPadding: hPadding
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,6 @@ SBSMessageBase {
|
||||||
Math.min(implicitWidth, innerContent.width - senderMargin)
|
Math.min(implicitWidth, innerContent.width - senderMargin)
|
||||||
}
|
}
|
||||||
|
|
||||||
height: implicitHeight
|
|
||||||
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
|
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
font.pixelSize: isEmojiOnly? JamiTheme.chatviewEmojiSize : JamiTheme.chatviewFontSize
|
font.pixelSize: isEmojiOnly? JamiTheme.chatviewEmojiSize : JamiTheme.chatviewFontSize
|
||||||
|
|
|
@ -487,6 +487,9 @@ Item {
|
||||||
property string troubleshootButton: qsTr("Open logs")
|
property string troubleshootButton: qsTr("Open logs")
|
||||||
property string troubleshootText: qsTr("Get logs")
|
property string troubleshootText: qsTr("Get logs")
|
||||||
|
|
||||||
|
property string experimentalCallSwarm: qsTr("(Experimental) Enable call support for swarm")
|
||||||
|
property string experimentalCallSwarmTooltip: qsTr("This feature will enable call buttons in swarms with multiple participants.")
|
||||||
|
|
||||||
// Recording Settings
|
// Recording Settings
|
||||||
property string tipRecordFolder: qsTr("Select a record directory")
|
property string tipRecordFolder: qsTr("Select a record directory")
|
||||||
property string quality: qsTr("Quality")
|
property string quality: qsTr("Quality")
|
||||||
|
@ -727,6 +730,15 @@ Item {
|
||||||
property string writeTo: qsTr("Write to %1")
|
property string writeTo: qsTr("Write to %1")
|
||||||
property string edit: qsTr("Edit")
|
property string edit: qsTr("Edit")
|
||||||
property string edited: qsTr("Edited")
|
property string edited: qsTr("Edited")
|
||||||
|
property string joinCall: qsTr("Join call")
|
||||||
|
property string wantToJoin: qsTr("A call is in progress. Do you want to join the call?")
|
||||||
|
property string needsHost: qsTr("Current host for this swarm seems unreachable. Do you want to host the call?")
|
||||||
|
property string chooseHoster: qsTr("Choose a dedicated device for hosting future calls in this swarm. If not set, the device starting a call will host it.")
|
||||||
|
property string chooseThisDevice: qsTr("Choose this device")
|
||||||
|
property string removeCurrentDevice: qsTr("Remove current device")
|
||||||
|
property string becomeHostOneCall: qsTr("Host only this call")
|
||||||
|
property string hostThisCall: qsTr("Host this call")
|
||||||
|
property string becomeDefaultHost: qsTr("Make me the default host for future calls")
|
||||||
|
|
||||||
// Invitation View
|
// Invitation View
|
||||||
property string invitationViewSentRequest: qsTr("%1 has sent you a request for a conversation.")
|
property string invitationViewSentRequest: qsTr("%1 has sent you a request for a conversation.")
|
||||||
|
@ -745,9 +757,11 @@ Item {
|
||||||
property string muteConversation: qsTr("Mute conversation")
|
property string muteConversation: qsTr("Mute conversation")
|
||||||
property string ignoreNotificationsTooltip: qsTr("Ignore all notifications from this conversation")
|
property string ignoreNotificationsTooltip: qsTr("Ignore all notifications from this conversation")
|
||||||
property string chooseAColor: qsTr("Choose a color")
|
property string chooseAColor: qsTr("Choose a color")
|
||||||
|
property string defaultCallHost: qsTr("Default host (calls)")
|
||||||
property string leaveTheSwarm: qsTr("Leave the swarm")
|
property string leaveTheSwarm: qsTr("Leave the swarm")
|
||||||
property string leave: qsTr("Leave")
|
property string leave: qsTr("Leave")
|
||||||
property string typeOfSwarm: qsTr("Type of swarm")
|
property string typeOfSwarm: qsTr("Type of swarm")
|
||||||
|
property string none: qsTr("None")
|
||||||
|
|
||||||
// NewSwarmPage
|
// NewSwarmPage
|
||||||
property string youCanAdd8: qsTr("You can add 8 people in the swarm")
|
property string youCanAdd8: qsTr("You can add 8 people in the swarm")
|
||||||
|
|
|
@ -94,6 +94,9 @@ ConversationListModelBase::dataForItem(item_t item, int role) const
|
||||||
return lrcInstance_->getContentDraft(item.uid, item.accountId);
|
return lrcInstance_->getContentDraft(item.uid, item.accountId);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
case Role::ActiveCallsCount: {
|
||||||
|
return item.activeCalls.size();
|
||||||
|
}
|
||||||
case Role::IsRequest:
|
case Role::IsRequest:
|
||||||
return QVariant(item.isRequest);
|
return QVariant(item.isRequest);
|
||||||
case Role::Title:
|
case Role::Title:
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
X(CallState) \
|
X(CallState) \
|
||||||
X(SectionName) \
|
X(SectionName) \
|
||||||
X(AccountId) \
|
X(AccountId) \
|
||||||
|
X(ActiveCallsCount) \
|
||||||
X(Draft) \
|
X(Draft) \
|
||||||
X(IsRequest) \
|
X(IsRequest) \
|
||||||
X(Mode) \
|
X(Mode) \
|
||||||
|
|
|
@ -59,7 +59,7 @@ ConversationsAdapter::ConversationsAdapter(SystemTray* systemTray,
|
||||||
} else {
|
} else {
|
||||||
// selected
|
// selected
|
||||||
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convId);
|
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convId);
|
||||||
if (convInfo.uid.isEmpty())
|
if (convInfo.uid.isEmpty() || convInfo.accountId != lrcInstance_->get_currentAccountId())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto& accInfo = lrcInstance_->getAccountInfo(convInfo.accountId);
|
auto& accInfo = lrcInstance_->getAccountInfo(convInfo.accountId);
|
||||||
|
@ -528,6 +528,16 @@ ConversationsAdapter::popFrontError(const QString& convId)
|
||||||
convModel->popFrontError(convId);
|
convModel->popFrontError(convId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConversationsAdapter::ignoreActiveCall(const QString& convId,
|
||||||
|
const QString& id,
|
||||||
|
const QString& uri,
|
||||||
|
const QString& device)
|
||||||
|
{
|
||||||
|
auto convModel = lrcInstance_->getCurrentConversationModel();
|
||||||
|
convModel->ignoreActiveCall(convId, id, uri, device);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ConversationsAdapter::updateConversationDescription(const QString& convId,
|
ConversationsAdapter::updateConversationDescription(const QString& convId,
|
||||||
const QString& newDescription)
|
const QString& newDescription)
|
||||||
|
|
|
@ -59,6 +59,10 @@ public:
|
||||||
Q_INVOKABLE void restartConversation(const QString& convId);
|
Q_INVOKABLE void restartConversation(const QString& convId);
|
||||||
Q_INVOKABLE void updateConversationTitle(const QString& convId, const QString& newTitle);
|
Q_INVOKABLE void updateConversationTitle(const QString& convId, const QString& newTitle);
|
||||||
Q_INVOKABLE void popFrontError(const QString& convId);
|
Q_INVOKABLE void popFrontError(const QString& convId);
|
||||||
|
Q_INVOKABLE void ignoreActiveCall(const QString& convId,
|
||||||
|
const QString& id,
|
||||||
|
const QString& uri,
|
||||||
|
const QString& device);
|
||||||
Q_INVOKABLE void updateConversationDescription(const QString& convId,
|
Q_INVOKABLE void updateConversationDescription(const QString& convId,
|
||||||
const QString& newDescription);
|
const QString& newDescription);
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,7 @@ CurrentAccount::updateData()
|
||||||
set_enabled(accInfo.enabled);
|
set_enabled(accInfo.enabled);
|
||||||
set_managerUri(accConfig.managerUri);
|
set_managerUri(accConfig.managerUri);
|
||||||
set_keepAliveEnabled(accConfig.keepAliveEnabled, true);
|
set_keepAliveEnabled(accConfig.keepAliveEnabled, true);
|
||||||
|
set_deviceId(accConfig.deviceId);
|
||||||
set_peerDiscovery(accConfig.peerDiscovery, true);
|
set_peerDiscovery(accConfig.peerDiscovery, true);
|
||||||
set_sendReadReceipt(accConfig.sendReadReceipt, true);
|
set_sendReadReceipt(accConfig.sendReadReceipt, true);
|
||||||
set_isRendezVous(accConfig.isRendezVous, true);
|
set_isRendezVous(accConfig.isRendezVous, true);
|
||||||
|
|
|
@ -101,6 +101,7 @@ class CurrentAccount final : public QObject
|
||||||
|
|
||||||
QML_RO_PROPERTY(QString, id)
|
QML_RO_PROPERTY(QString, id)
|
||||||
QML_RO_PROPERTY(QString, uri)
|
QML_RO_PROPERTY(QString, uri)
|
||||||
|
QML_RO_PROPERTY(QString, deviceId)
|
||||||
QML_RO_PROPERTY(QString, registeredName)
|
QML_RO_PROPERTY(QString, registeredName)
|
||||||
QML_RO_PROPERTY(QString, alias)
|
QML_RO_PROPERTY(QString, alias)
|
||||||
QML_RO_PROPERTY(QString, bestId)
|
QML_RO_PROPERTY(QString, bestId)
|
||||||
|
|
|
@ -53,8 +53,6 @@ CurrentConversation::updateData()
|
||||||
const auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
|
const auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
|
||||||
if (auto optConv = accInfo.conversationModel->getConversationForUid(convId)) {
|
if (auto optConv = accInfo.conversationModel->getConversationForUid(convId)) {
|
||||||
auto& convInfo = optConv->get();
|
auto& convInfo = optConv->get();
|
||||||
set_title(accInfo.conversationModel->title(convId));
|
|
||||||
set_description(accInfo.conversationModel->description(convId));
|
|
||||||
set_uris(convInfo.participantsUris());
|
set_uris(convInfo.participantsUris());
|
||||||
set_isSwarm(convInfo.isSwarm());
|
set_isSwarm(convInfo.isSwarm());
|
||||||
set_isLegacy(convInfo.isLegacy());
|
set_isLegacy(convInfo.isLegacy());
|
||||||
|
@ -104,6 +102,9 @@ CurrentConversation::updateData()
|
||||||
} else if (convInfo.mode == conversation::Mode::PUBLIC) {
|
} else if (convInfo.mode == conversation::Mode::PUBLIC) {
|
||||||
set_modeString(tr("Public group"));
|
set_modeString(tr("Public group"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onProfileUpdated(convId);
|
||||||
|
updateActiveCalls(accountId, convId);
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
qWarning() << "Can't update current conversation data for" << convId;
|
qWarning() << "Can't update current conversation data for" << convId;
|
||||||
|
@ -111,33 +112,58 @@ CurrentConversation::updateData()
|
||||||
updateErrors(convId);
|
updateErrors(convId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CurrentConversation::onNeedsHost(const QString& convId)
|
||||||
|
{
|
||||||
|
if (id_ != convId)
|
||||||
|
return;
|
||||||
|
Q_EMIT needsHost();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CurrentConversation::setPreference(const QString& key, const QString& value)
|
CurrentConversation::setPreference(const QString& key, const QString& value)
|
||||||
{
|
{
|
||||||
|
if (key == "color")
|
||||||
|
set_color(value);
|
||||||
|
auto preferences = getPreferences();
|
||||||
|
preferences[key] = value;
|
||||||
auto accountId = lrcInstance_->get_currentAccountId();
|
auto accountId = lrcInstance_->get_currentAccountId();
|
||||||
const auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
|
const auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
|
||||||
auto convId = lrcInstance_->get_selectedConvUid();
|
auto convId = lrcInstance_->get_selectedConvUid();
|
||||||
if (auto optConv = accInfo.conversationModel->getConversationForUid(convId)) {
|
|
||||||
auto& convInfo = optConv->get();
|
|
||||||
auto preferences = convInfo.preferences;
|
|
||||||
preferences[key] = value;
|
|
||||||
accInfo.conversationModel->setConversationPreferences(convId, preferences);
|
accInfo.conversationModel->setConversationPreferences(convId, preferences);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
CurrentConversation::getPreference(const QString& key) const
|
CurrentConversation::getPreference(const QString& key) const
|
||||||
|
{
|
||||||
|
return getPreferences()[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
MapStringString
|
||||||
|
CurrentConversation::getPreferences() const
|
||||||
{
|
{
|
||||||
auto accountId = lrcInstance_->get_currentAccountId();
|
auto accountId = lrcInstance_->get_currentAccountId();
|
||||||
const auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
|
const auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
|
||||||
auto convId = lrcInstance_->get_selectedConvUid();
|
auto convId = lrcInstance_->get_selectedConvUid();
|
||||||
if (auto optConv = accInfo.conversationModel->getConversationForUid(convId)) {
|
if (auto optConv = accInfo.conversationModel->getConversationForUid(convId)) {
|
||||||
auto& convInfo = optConv->get();
|
auto& convInfo = optConv->get();
|
||||||
return convInfo.preferences[key];
|
auto preferences = accInfo.conversationModel->getConversationPreferences(convId);
|
||||||
|
return preferences;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CurrentConversation::setInfo(const QString& key, const QString& value)
|
||||||
|
{
|
||||||
|
MapStringString infos;
|
||||||
|
infos[key] = value;
|
||||||
|
auto accountId = lrcInstance_->get_currentAccountId();
|
||||||
|
const auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
|
||||||
|
auto convId = lrcInstance_->get_selectedConvUid();
|
||||||
|
accInfo.conversationModel->updateConversationInfos(convId, infos);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CurrentConversation::onConversationUpdated(const QString& convId)
|
CurrentConversation::onConversationUpdated(const QString& convId)
|
||||||
{
|
{
|
||||||
|
@ -153,8 +179,27 @@ CurrentConversation::onProfileUpdated(const QString& convId)
|
||||||
// filter for our currently set id
|
// filter for our currently set id
|
||||||
if (id_ != convId)
|
if (id_ != convId)
|
||||||
return;
|
return;
|
||||||
set_title(lrcInstance_->getCurrentConversationModel()->title(convId));
|
const auto& convModel = lrcInstance_->getCurrentConversationModel();
|
||||||
set_description(lrcInstance_->getCurrentConversationModel()->description(convId));
|
set_title(convModel->title(convId));
|
||||||
|
set_description(convModel->description(convId));
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (auto optConv = convModel->getConversationForUid(convId)) {
|
||||||
|
auto& convInfo = optConv->get();
|
||||||
|
// Now, update call informations (rdvAccount/device)
|
||||||
|
if (convInfo.infos.contains("rdvAccount")) {
|
||||||
|
set_rdvAccount(convInfo.infos["rdvAccount"]);
|
||||||
|
} else {
|
||||||
|
set_rdvAccount("");
|
||||||
|
}
|
||||||
|
if (convInfo.infos.contains("rdvDevice")) {
|
||||||
|
set_rdvDevice(convInfo.infos["rdvDevice"]);
|
||||||
|
} else {
|
||||||
|
set_rdvDevice("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -200,11 +245,21 @@ CurrentConversation::connectModel()
|
||||||
this,
|
this,
|
||||||
&CurrentConversation::updateErrors,
|
&CurrentConversation::updateErrors,
|
||||||
Qt::UniqueConnection);
|
Qt::UniqueConnection);
|
||||||
|
connect(lrcInstance_->getCurrentConversationModel(),
|
||||||
|
&ConversationModel::activeCallsChanged,
|
||||||
|
this,
|
||||||
|
&CurrentConversation::updateActiveCalls,
|
||||||
|
Qt::UniqueConnection);
|
||||||
connect(lrcInstance_->getCurrentConversationModel(),
|
connect(lrcInstance_->getCurrentConversationModel(),
|
||||||
&ConversationModel::conversationPreferencesUpdated,
|
&ConversationModel::conversationPreferencesUpdated,
|
||||||
this,
|
this,
|
||||||
&CurrentConversation::updateConversationPreferences,
|
&CurrentConversation::updateConversationPreferences,
|
||||||
Qt::UniqueConnection);
|
Qt::UniqueConnection);
|
||||||
|
connect(lrcInstance_->getCurrentConversationModel(),
|
||||||
|
&ConversationModel::needsHost,
|
||||||
|
this,
|
||||||
|
&CurrentConversation::onNeedsHost,
|
||||||
|
Qt::UniqueConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -246,6 +301,42 @@ CurrentConversation::updateErrors(const QString& convId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CurrentConversation::updateActiveCalls(const QString&, const QString& convId)
|
||||||
|
{
|
||||||
|
if (convId != id_)
|
||||||
|
return;
|
||||||
|
const auto& convModel = lrcInstance_->getCurrentConversationModel();
|
||||||
|
if (auto optConv = convModel->getConversationForUid(convId)) {
|
||||||
|
auto& convInfo = optConv->get();
|
||||||
|
QVariantList callList;
|
||||||
|
for (int i = 0; i < convInfo.activeCalls.size(); i++) {
|
||||||
|
// Check if ignored.
|
||||||
|
auto ignored = false;
|
||||||
|
for (int ignoredIdx = 0; ignoredIdx < convInfo.ignoredActiveCalls.size(); ignoredIdx++) {
|
||||||
|
auto& ignoreCall = convInfo.ignoredActiveCalls[ignoredIdx];
|
||||||
|
if (ignoreCall["id"] == convInfo.activeCalls[i]["id"]
|
||||||
|
&& ignoreCall["uri"] == convInfo.activeCalls[i]["uri"]
|
||||||
|
&& ignoreCall["device"] == convInfo.activeCalls[i]["device"]) {
|
||||||
|
ignored = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ignored) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, add to model
|
||||||
|
QVariantMap mapCall;
|
||||||
|
Q_FOREACH (QString key, convInfo.activeCalls[i].keys()) {
|
||||||
|
mapCall[key] = convInfo.activeCalls[i][key];
|
||||||
|
}
|
||||||
|
callList.append(mapCall);
|
||||||
|
}
|
||||||
|
set_activeCalls(callList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CurrentConversation::scrollToMsg(const QString& msg)
|
CurrentConversation::scrollToMsg(const QString& msg)
|
||||||
{
|
{
|
||||||
|
|
|
@ -43,12 +43,15 @@ class CurrentConversation final : public QObject
|
||||||
QML_PROPERTY(bool, ignoreNotifications)
|
QML_PROPERTY(bool, ignoreNotifications)
|
||||||
QML_PROPERTY(QString, callId)
|
QML_PROPERTY(QString, callId)
|
||||||
QML_PROPERTY(QString, color)
|
QML_PROPERTY(QString, color)
|
||||||
|
QML_PROPERTY(QString, rdvAccount)
|
||||||
|
QML_PROPERTY(QString, rdvDevice)
|
||||||
QML_PROPERTY(call::Status, callState)
|
QML_PROPERTY(call::Status, callState)
|
||||||
QML_PROPERTY(bool, inCall)
|
QML_PROPERTY(bool, inCall)
|
||||||
QML_PROPERTY(bool, isTemporary)
|
QML_PROPERTY(bool, isTemporary)
|
||||||
QML_PROPERTY(bool, isContact)
|
QML_PROPERTY(bool, isContact)
|
||||||
QML_PROPERTY(bool, allMessagesLoaded)
|
QML_PROPERTY(bool, allMessagesLoaded)
|
||||||
QML_PROPERTY(QString, modeString)
|
QML_PROPERTY(QString, modeString)
|
||||||
|
QML_PROPERTY(QVariantList, activeCalls)
|
||||||
QML_PROPERTY(QStringList, errors)
|
QML_PROPERTY(QStringList, errors)
|
||||||
QML_PROPERTY(QStringList, backendErrors)
|
QML_PROPERTY(QStringList, backendErrors)
|
||||||
|
|
||||||
|
@ -63,6 +66,8 @@ public:
|
||||||
Q_INVOKABLE void showSwarmDetails() const;
|
Q_INVOKABLE void showSwarmDetails() const;
|
||||||
Q_INVOKABLE void setPreference(const QString& key, const QString& value);
|
Q_INVOKABLE void setPreference(const QString& key, const QString& value);
|
||||||
Q_INVOKABLE QString getPreference(const QString& key) const;
|
Q_INVOKABLE QString getPreference(const QString& key) const;
|
||||||
|
Q_INVOKABLE MapStringString getPreferences() const;
|
||||||
|
Q_INVOKABLE void setInfo(const QString& key, const QString& value);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void scrollTo(const QString& msgId);
|
void scrollTo(const QString& msgId);
|
||||||
|
@ -70,10 +75,15 @@ Q_SIGNALS:
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void updateData();
|
void updateData();
|
||||||
|
void onNeedsHost(const QString& convId);
|
||||||
void onConversationUpdated(const QString& convId);
|
void onConversationUpdated(const QString& convId);
|
||||||
void onProfileUpdated(const QString& convId);
|
void onProfileUpdated(const QString& convId);
|
||||||
void updateErrors(const QString& convId);
|
void updateErrors(const QString& convId);
|
||||||
void updateConversationPreferences(const QString& convId);
|
void updateConversationPreferences(const QString& convId);
|
||||||
|
void updateActiveCalls(const QString&, const QString& convId);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void needsHost();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LRCInstance* lrcInstance_;
|
LRCInstance* lrcInstance_;
|
||||||
|
|
|
@ -190,9 +190,8 @@ LRCInstance::getCallInfoForConversation(const conversation::Info& convInfo, bool
|
||||||
auto callId = forceCallOnly
|
auto callId = forceCallOnly
|
||||||
? convInfo.callId
|
? convInfo.callId
|
||||||
: (convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId);
|
: (convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId);
|
||||||
if (!accInfo.callModel->hasCall(callId)) {
|
if (!accInfo.callModel->hasCall(callId))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
|
||||||
return &accInfo.callModel->getCall(callId);
|
return &accInfo.callModel->getCall(callId);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -372,6 +371,16 @@ LRCInstance::selectConversation(const QString& convId, const QString& accountId)
|
||||||
set_selectedConvUid(convId);
|
set_selectedConvUid(convId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LRCInstance::indexOfActiveCall(const QString& confId, const QString& uri, const QString& deviceId)
|
||||||
|
{
|
||||||
|
if (auto optConv = getCurrentConversationModel()->getConversationForUid(selectedConvUid_)) {
|
||||||
|
auto& convInfo = optConv->get();
|
||||||
|
return convInfo.indexOfActiveCall({{"confId", confId}, {"uri", uri}, {"device", deviceId}});
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LRCInstance::deselectConversation()
|
LRCInstance::deselectConversation()
|
||||||
{
|
{
|
||||||
|
|
|
@ -108,6 +108,9 @@ public:
|
||||||
Q_INVOKABLE void setContentDraft(const QString& convUid,
|
Q_INVOKABLE void setContentDraft(const QString& convUid,
|
||||||
const QString& accountId,
|
const QString& accountId,
|
||||||
const QString& content);
|
const QString& content);
|
||||||
|
Q_INVOKABLE int indexOfActiveCall(const QString& confId,
|
||||||
|
const QString& uri,
|
||||||
|
const QString& deviceId);
|
||||||
|
|
||||||
int getCurrentAccountIndex();
|
int getCurrentAccountIndex();
|
||||||
void setCurrAccDisplayName(const QString& displayName);
|
void setCurrAccDisplayName(const QString& displayName);
|
||||||
|
|
|
@ -47,6 +47,10 @@ Rectangle {
|
||||||
|
|
||||||
color: JamiTheme.chatviewBgColor
|
color: JamiTheme.chatviewBgColor
|
||||||
|
|
||||||
|
HostPopup {
|
||||||
|
id: hostPopup
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: root
|
anchors.fill: root
|
||||||
|
|
||||||
|
@ -88,6 +92,10 @@ Rectangle {
|
||||||
addMemberPanel.visible = !addMemberPanel.visible
|
addMemberPanel.visible = !addMemberPanel.visible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onNeedsHost() {
|
||||||
|
hostPopup.open()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddToConversationClicked: {
|
onAddToConversationClicked: {
|
||||||
|
@ -105,9 +113,38 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: CurrentConversation
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
function onActiveCallsChanged() {
|
||||||
|
if (CurrentConversation.activeCalls.length > 0) {
|
||||||
|
notificationArea.id = CurrentConversation.activeCalls[0]["id"]
|
||||||
|
notificationArea.uri = CurrentConversation.activeCalls[0]["uri"]
|
||||||
|
notificationArea.device = CurrentConversation.activeCalls[0]["device"]
|
||||||
|
}
|
||||||
|
notificationArea.visible = CurrentConversation.activeCalls.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function onErrorsChanged() {
|
||||||
|
if (CurrentConversation.errors.length > 0) {
|
||||||
|
errorRect.errorLabel.text = CurrentConversation.errors[0]
|
||||||
|
errorRect.backendErrorToolTip.text = JamiStrings.backendError.arg(CurrentConversation.backendErrors[0])
|
||||||
|
}
|
||||||
|
errorRect.visible = CurrentConversation.errors.length > 0 // If too much noise: && LRCInstance.debugMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ConversationErrorsRow {
|
ConversationErrorsRow {
|
||||||
id: errorRect
|
id: errorRect
|
||||||
color: JamiTheme.filterBadgeColor
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: JamiTheme.chatViewHeaderPreferredHeight
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationArea {
|
||||||
|
id: notificationArea
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: JamiTheme.chatViewHeaderPreferredHeight
|
Layout.preferredHeight: JamiTheme.chatViewHeaderPreferredHeight
|
||||||
visible: false
|
visible: false
|
||||||
|
|
|
@ -20,9 +20,10 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import net.jami.Models 1.1
|
|
||||||
import net.jami.Constants 1.1
|
|
||||||
import net.jami.Adapters 1.1
|
import net.jami.Adapters 1.1
|
||||||
|
import net.jami.Constants 1.1
|
||||||
|
import net.jami.Enums 1.1
|
||||||
|
import net.jami.Models 1.1
|
||||||
|
|
||||||
import "../../commoncomponents"
|
import "../../commoncomponents"
|
||||||
|
|
||||||
|
@ -155,7 +156,7 @@ Rectangle {
|
||||||
PushButton {
|
PushButton {
|
||||||
id: startAAudioCallButton
|
id: startAAudioCallButton
|
||||||
|
|
||||||
visible: interactionButtonsVisibility && !addMemberVisibility
|
visible: interactionButtonsVisibility && (!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
|
||||||
|
|
||||||
source: JamiResources.place_audiocall_24dp_svg
|
source: JamiResources.place_audiocall_24dp_svg
|
||||||
toolTipText: JamiStrings.placeAudioCall
|
toolTipText: JamiStrings.placeAudioCall
|
||||||
|
@ -169,7 +170,7 @@ Rectangle {
|
||||||
PushButton {
|
PushButton {
|
||||||
id: startAVideoCallButton
|
id: startAVideoCallButton
|
||||||
|
|
||||||
visible: CurrentAccount.videoEnabled_Video && interactionButtonsVisibility && !addMemberVisibility
|
visible: CurrentAccount.videoEnabled_Video && interactionButtonsVisibility && (!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
|
||||||
source: JamiResources.videocam_24dp_svg
|
source: JamiResources.videocam_24dp_svg
|
||||||
toolTipText: JamiStrings.placeVideoCall
|
toolTipText: JamiStrings.placeVideoCall
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ Rectangle {
|
||||||
errorRect.visible = CurrentConversation.errors.length > 0 && LRCInstance.debugMode()
|
errorRect.visible = CurrentConversation.errors.length > 0 && LRCInstance.debugMode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
color: JamiTheme.filterBadgeColor
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
219
src/app/mainview/components/DevicesListPopup.qml
Normal file
219
src/app/mainview/components/DevicesListPopup.qml
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020-2022 Savoir-faire Linux Inc.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import net.jami.Models 1.1
|
||||||
|
import net.jami.Adapters 1.1
|
||||||
|
import net.jami.Constants 1.1
|
||||||
|
|
||||||
|
import "../../commoncomponents"
|
||||||
|
|
||||||
|
BaseModalDialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
width: 488
|
||||||
|
height: 320
|
||||||
|
|
||||||
|
popupContent: Rectangle {
|
||||||
|
id: rect
|
||||||
|
|
||||||
|
color: JamiTheme.transparentColor
|
||||||
|
width: root.width
|
||||||
|
|
||||||
|
|
||||||
|
PushButton {
|
||||||
|
id: btnCancel
|
||||||
|
imageColor: "grey"
|
||||||
|
normalColor: "transparent"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
source: JamiResources.round_close_24dp_svg
|
||||||
|
onClicked: { close(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mainLayout
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: JamiTheme.preferredMarginSize
|
||||||
|
spacing: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: informativeLabel
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 26
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
text: JamiStrings.chooseHoster
|
||||||
|
color: JamiTheme.primaryForegroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
JamiListView {
|
||||||
|
id: devicesListView
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 160
|
||||||
|
|
||||||
|
model: SortFilterProxyModel {
|
||||||
|
sourceModel: DeviceItemListModel
|
||||||
|
sorters: [
|
||||||
|
RoleSorter { roleName: "IsCurrent"; sortOrder: Qt.DescendingOrder },
|
||||||
|
StringSorter {
|
||||||
|
roleName: "DeviceName"
|
||||||
|
caseSensitivity: Qt.CaseInsensitive
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ItemDelegate {
|
||||||
|
id: item
|
||||||
|
|
||||||
|
property string deviceName : DeviceName
|
||||||
|
property string deviceId : DeviceID
|
||||||
|
property bool isCurrent : DeviceName
|
||||||
|
|
||||||
|
implicitWidth: devicesListView.width
|
||||||
|
width: devicesListView.width
|
||||||
|
height: 70
|
||||||
|
|
||||||
|
highlighted: ListView.isCurrentItem
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
devicesListView.currentIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: highlighted? JamiTheme.selectedColor : JamiTheme.editBackgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: item
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: deviceImage
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredWidth: 24
|
||||||
|
Layout.preferredHeight: 24
|
||||||
|
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
layer {
|
||||||
|
enabled: true
|
||||||
|
effect: ColorOverlay {
|
||||||
|
color: JamiTheme.textColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
source: JamiResources.baseline_desktop_windows_24dp_svg
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: deviceInfoColumnLayout
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: labelDeviceName
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: JamiTheme.textColor
|
||||||
|
text: deviceName
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: labelDeviceId
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: JamiTheme.textColor
|
||||||
|
text: deviceId === "" ? qsTr("Device Id") : deviceId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomBorder {
|
||||||
|
commonBorder: false
|
||||||
|
lBorderwidth: 0
|
||||||
|
rBorderwidth: 0
|
||||||
|
tBorderwidth: 0
|
||||||
|
bBorderwidth: 2
|
||||||
|
borderColor: JamiTheme.selectedColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: JamiTheme.preferredMarginSize
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
|
||||||
|
MaterialButton {
|
||||||
|
id: chooseBtn
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
enabled: devicesListView.currentItem
|
||||||
|
|
||||||
|
text: JamiStrings.chooseThisDevice
|
||||||
|
toolTipText: JamiStrings.chooseThisDevice
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
CurrentConversation.setInfo("rdvAccount", CurrentAccount.uri)
|
||||||
|
CurrentConversation.setInfo("rdvDevice", devicesListView.currentItem.deviceId)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialButton {
|
||||||
|
id: rmDeviceBtn
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
enabled: devicesListView.currentItem
|
||||||
|
|
||||||
|
text: JamiStrings.removeCurrentDevice
|
||||||
|
toolTipText: JamiStrings.removeCurrentDevice
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
CurrentConversation.setInfo("rdvAccount", "")
|
||||||
|
CurrentConversation.setInfo("rdvDevice", "")
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
src/app/mainview/components/HostPopup.qml
Normal file
111
src/app/mainview/components/HostPopup.qml
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Savoir-faire Linux Inc.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
|
import net.jami.Models 1.1
|
||||||
|
import net.jami.Adapters 1.1
|
||||||
|
import net.jami.Constants 1.1
|
||||||
|
|
||||||
|
import "../../commoncomponents"
|
||||||
|
|
||||||
|
BaseModalDialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
width: 488
|
||||||
|
height: 256
|
||||||
|
|
||||||
|
property bool isAdmin: {
|
||||||
|
var role = UtilsAdapter.getParticipantRole(CurrentAccount.id, CurrentConversation.id, CurrentAccount.uri)
|
||||||
|
return role === Member.Role.ADMIN
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
popupContent: Rectangle {
|
||||||
|
id: rect
|
||||||
|
|
||||||
|
color: JamiTheme.transparentColor
|
||||||
|
width: root.width
|
||||||
|
|
||||||
|
|
||||||
|
PushButton {
|
||||||
|
id: btnCancel
|
||||||
|
imageColor: "grey"
|
||||||
|
normalColor: "transparent"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
source: JamiResources.round_close_24dp_svg
|
||||||
|
onClicked: { close();}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mainLayout
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: informativeLabel
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 26
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
text: JamiStrings.needsHost
|
||||||
|
color: JamiTheme.primaryForegroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialButton {
|
||||||
|
id: becomeHostBtn
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
|
||||||
|
Layout.topMargin: 26
|
||||||
|
text: isAdmin? JamiStrings.becomeHostOneCall : JamiStrings.hostThisCall
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
MessagesAdapter.joinCall(CurrentAccount.uri, CurrentAccount.deviceId, "0")
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialButton {
|
||||||
|
id: becomeDefaultHostBtn
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
|
||||||
|
text: JamiStrings.becomeDefaultHost
|
||||||
|
toolTipText: JamiStrings.becomeDefaultHost
|
||||||
|
|
||||||
|
visible: isAdmin
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
CurrentConversation.setInfo("rdvAccount", CurrentAccount.uri)
|
||||||
|
CurrentConversation.setInfo("rdvDevice", devicesListView.currentItem.deviceId)
|
||||||
|
MessagesAdapter.joinCall(CurrentAccount.uri, CurrentAccount.deviceId, "0")
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -241,7 +241,7 @@ JamiListView {
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: Interaction.Type.CALL
|
roleValue: Interaction.Type.CALL
|
||||||
|
|
||||||
GeneratedMessageDelegate {
|
CallMessageDelegate {
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
computeChatview(this, index)
|
computeChatview(this, index)
|
||||||
}
|
}
|
||||||
|
|
117
src/app/mainview/components/NotificationArea.qml
Normal file
117
src/app/mainview/components/NotificationArea.qml
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Savoir-faire Linux Inc.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
|
import net.jami.Models 1.1
|
||||||
|
import net.jami.Adapters 1.1
|
||||||
|
import net.jami.Constants 1.1
|
||||||
|
|
||||||
|
import "../../commoncomponents"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
opacity: visible
|
||||||
|
color: CurrentConversation.color
|
||||||
|
|
||||||
|
property string id: ""
|
||||||
|
property string uri: ""
|
||||||
|
property string device: ""
|
||||||
|
|
||||||
|
property string textColor: UtilsAdapter.luma(root.color) ?
|
||||||
|
JamiTheme.chatviewTextColorLight :
|
||||||
|
JamiTheme.chatviewTextColorDark
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: JamiTheme.preferredMarginSize
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: errorLabel
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.margins: 0
|
||||||
|
text: JamiStrings.wantToJoin
|
||||||
|
color: root.textColor
|
||||||
|
font.pixelSize: JamiTheme.headerFontSize
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: controls
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
|
||||||
|
PushButton {
|
||||||
|
id: joinCallInAudio
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
Layout.rightMargin: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
source: JamiResources.place_audiocall_24dp_svg
|
||||||
|
toolTipText: JamiStrings.joinCall
|
||||||
|
|
||||||
|
imageColor: root.textColor
|
||||||
|
normalColor: "transparent"
|
||||||
|
hoveredColor: Qt.rgba(255, 255, 255, 0.2)
|
||||||
|
border.width: 1
|
||||||
|
border.color: root.textColor
|
||||||
|
|
||||||
|
onClicked: MessagesAdapter.joinCall(uri, device, id, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PushButton {
|
||||||
|
id: joinCallInVideo
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
Layout.rightMargin: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
source: JamiResources.videocam_24dp_svg
|
||||||
|
toolTipText: JamiStrings.joinCall
|
||||||
|
|
||||||
|
imageColor: root.textColor
|
||||||
|
normalColor: "transparent"
|
||||||
|
hoveredColor: Qt.rgba(255, 255, 255, 0.2)
|
||||||
|
border.width: 1
|
||||||
|
border.color: root.textColor
|
||||||
|
visible: CurrentAccount.videoEnabled_Video
|
||||||
|
|
||||||
|
onClicked: MessagesAdapter.joinCall(uri, device, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
PushButton {
|
||||||
|
id: btnClose
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
|
||||||
|
imageColor: root.textColor
|
||||||
|
normalColor: JamiTheme.transparentColor
|
||||||
|
|
||||||
|
source: JamiResources.round_close_24dp_svg
|
||||||
|
|
||||||
|
onClicked: ConversationsAdapter.ignoreActiveCall(CurrentConversation.id, id, uri, device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
from: 0
|
||||||
|
duration: JamiTheme.shortFadeDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,9 +22,10 @@ import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
import net.jami.Models 1.1
|
|
||||||
import net.jami.Adapters 1.1
|
import net.jami.Adapters 1.1
|
||||||
import net.jami.Constants 1.1
|
import net.jami.Constants 1.1
|
||||||
|
import net.jami.Enums 1.1
|
||||||
|
import net.jami.Models 1.1
|
||||||
|
|
||||||
import "../../commoncomponents"
|
import "../../commoncomponents"
|
||||||
|
|
||||||
|
@ -148,11 +149,6 @@ ItemDelegate {
|
||||||
font.hintingPreference: Font.PreferNoHinting
|
font.hintingPreference: Font.PreferNoHinting
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
color: JamiTheme.textColor
|
color: JamiTheme.textColor
|
||||||
// deal with poor rendering of the pencil emoji on Windows
|
|
||||||
font.family: Qt.platform.os === "windows" && Draft ?
|
|
||||||
"Segoe UI Emoji" :
|
|
||||||
Qt.application.font.family
|
|
||||||
lineHeight: font.family === "Segoe UI Emoji" ? 1.25 : 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
|
@ -175,6 +171,13 @@ ItemDelegate {
|
||||||
color: JamiTheme.primaryForegroundColor
|
color: JamiTheme.primaryForegroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show that a call is ongoing for groups indicator
|
||||||
|
ResponsiveImage {
|
||||||
|
visible: ActiveCallsCount && !root.highlighted
|
||||||
|
source: JamiResources.videocam_24dp_svg
|
||||||
|
color: JamiTheme.primaryForegroundColor
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: 2
|
spacing: 2
|
||||||
|
@ -232,6 +235,8 @@ ItemDelegate {
|
||||||
if (!interactive)
|
if (!interactive)
|
||||||
return;
|
return;
|
||||||
ListView.view.model.select(index)
|
ListView.view.model.select(index)
|
||||||
|
if (CurrentConversation.isSwarm && !CurrentConversation.isCoreDialog && !UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
|
||||||
|
return; // For now disable calls for swarm with multiple participants
|
||||||
if (LRCInstance.currentAccountType === Profile.Type.SIP || !CurrentAccount.videoEnabled_Video)
|
if (LRCInstance.currentAccountType === Profile.Type.SIP || !CurrentAccount.videoEnabled_Video)
|
||||||
CallAdapter.placeAudioOnlyCall()
|
CallAdapter.placeAudioOnlyCall()
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -35,6 +35,11 @@ Rectangle {
|
||||||
color: CurrentConversation.color
|
color: CurrentConversation.color
|
||||||
property var isAdmin: UtilsAdapter.getParticipantRole(CurrentAccount.id, CurrentConversation.id, CurrentAccount.uri) === Member.Role.ADMIN
|
property var isAdmin: UtilsAdapter.getParticipantRole(CurrentAccount.id, CurrentConversation.id, CurrentAccount.uri) === Member.Role.ADMIN
|
||||||
|
|
||||||
|
|
||||||
|
DevicesListPopup {
|
||||||
|
id: devicesListPopup
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: swarmProfileDetails
|
id: swarmProfileDetails
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
@ -343,6 +348,114 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SwarmDetailsItem {
|
||||||
|
id: settingsSwarmItem
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: JamiTheme.settingsFontSize + 2 * JamiTheme.preferredMarginSize + 4
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: settingsSwarmText
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 30
|
||||||
|
Layout.rightMargin: JamiTheme.preferredMarginSize
|
||||||
|
Layout.maximumWidth: settingsSwarmItem.width / 2
|
||||||
|
|
||||||
|
text: JamiStrings.defaultCallHost
|
||||||
|
font.pointSize: JamiTheme.settingsFontSize
|
||||||
|
font.kerning: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
color: JamiTheme.textColor
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: swarmRdvPref
|
||||||
|
spacing: 10
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
Layout.maximumWidth: settingsSwarmItem.width / 2
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: CurrentConversation
|
||||||
|
|
||||||
|
function onRdvAccountChanged() {
|
||||||
|
// This avoid incorrect avatar by always modifying the mode before the imageId
|
||||||
|
avatar.mode = CurrentConversation.rdvAccount === CurrentAccount.uri ? Avatar.Mode.Account : Avatar.Mode.Contact
|
||||||
|
avatar.imageId = CurrentConversation.rdvAccount === CurrentAccount.uri ? CurrentAccount.id : CurrentConversation.rdvAccount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Avatar {
|
||||||
|
id: avatar
|
||||||
|
width: JamiTheme.contactMessageAvatarSize
|
||||||
|
height: JamiTheme.contactMessageAvatarSize
|
||||||
|
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||||
|
Layout.topMargin: JamiTheme.preferredMarginSize / 2
|
||||||
|
visible: CurrentConversation.rdvAccount !== ""
|
||||||
|
|
||||||
|
imageId: ""
|
||||||
|
showPresenceIndicator: false
|
||||||
|
mode: Avatar.Mode.Account
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
ElidedTextLabel {
|
||||||
|
id: bestName
|
||||||
|
|
||||||
|
eText: {
|
||||||
|
if (CurrentConversation.rdvAccount === "")
|
||||||
|
return JamiStrings.none
|
||||||
|
else if (CurrentConversation.rdvAccount === CurrentAccount.uri)
|
||||||
|
return CurrentAccount.bestName
|
||||||
|
else
|
||||||
|
return UtilsAdapter.getBestNameForUri(CurrentAccount.id, CurrentConversation.rdvAccount)
|
||||||
|
}
|
||||||
|
maxWidth: JamiTheme.preferredFieldWidth
|
||||||
|
|
||||||
|
font.pointSize: JamiTheme.participantFontSize
|
||||||
|
color: JamiTheme.primaryForegroundColor
|
||||||
|
font.kerning: true
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
ElidedTextLabel {
|
||||||
|
id: deviceId
|
||||||
|
|
||||||
|
eText: CurrentConversation.rdvDevice === "" ? JamiStrings.none : CurrentConversation.rdvDevice
|
||||||
|
visible: CurrentConversation.rdvDevice !== ""
|
||||||
|
maxWidth: JamiTheme.preferredFieldWidth
|
||||||
|
|
||||||
|
font.pointSize: JamiTheme.participantFontSize
|
||||||
|
color: JamiTheme.textColorHovered
|
||||||
|
font.kerning: true
|
||||||
|
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
target: parent
|
||||||
|
|
||||||
|
enabled: parent.visible && root.isAdmin
|
||||||
|
onTapped: function onTapped(eventPoint) {
|
||||||
|
devicesListPopup.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.leftMargin: JamiTheme.preferredMarginSize
|
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||||
Layout.preferredHeight: JamiTheme.settingsFontSize + 2 * JamiTheme.preferredMarginSize + 4
|
Layout.preferredHeight: JamiTheme.settingsFontSize + 2 * JamiTheme.preferredMarginSize + 4
|
||||||
|
|
|
@ -200,6 +200,19 @@ MessagesAdapter::retryInteraction(const QString& interactionId)
|
||||||
->retryInteraction(lrcInstance_->get_selectedConvUid(), interactionId);
|
->retryInteraction(lrcInstance_->get_selectedConvUid(), interactionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MessagesAdapter::joinCall(const QString& uri,
|
||||||
|
const QString& deviceId,
|
||||||
|
const QString& confId,
|
||||||
|
bool isAudioOnly)
|
||||||
|
{
|
||||||
|
lrcInstance_->getCurrentConversationModel()->joinCall(lrcInstance_->get_selectedConvUid(),
|
||||||
|
uri,
|
||||||
|
deviceId,
|
||||||
|
confId,
|
||||||
|
isAudioOnly);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MessagesAdapter::copyToDownloads(const QString& interactionId, const QString& displayName)
|
MessagesAdapter::copyToDownloads(const QString& interactionId, const QString& displayName)
|
||||||
{
|
{
|
||||||
|
|
|
@ -79,6 +79,10 @@ protected:
|
||||||
Q_INVOKABLE void openDirectory(const QString& arg);
|
Q_INVOKABLE void openDirectory(const QString& arg);
|
||||||
Q_INVOKABLE void retryInteraction(const QString& interactionId);
|
Q_INVOKABLE void retryInteraction(const QString& interactionId);
|
||||||
Q_INVOKABLE void deleteInteraction(const QString& interactionId);
|
Q_INVOKABLE void deleteInteraction(const QString& interactionId);
|
||||||
|
Q_INVOKABLE void joinCall(const QString& uri,
|
||||||
|
const QString& deviceId,
|
||||||
|
const QString& confId,
|
||||||
|
bool isAudioOnly = false);
|
||||||
Q_INVOKABLE void copyToDownloads(const QString& interactionId, const QString& displayName);
|
Q_INVOKABLE void copyToDownloads(const QString& interactionId, const QString& displayName);
|
||||||
Q_INVOKABLE void userIsComposing(bool isComposing);
|
Q_INVOKABLE void userIsComposing(bool isComposing);
|
||||||
Q_INVOKABLE QVariantMap isLocalImage(const QString& mimeName);
|
Q_INVOKABLE QVariantMap isLocalImage(const QString& mimeName);
|
||||||
|
|
|
@ -85,4 +85,17 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToggleSwitch {
|
||||||
|
id: checkboxCallSwarm
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||||
|
checked: UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm)
|
||||||
|
labelText: JamiStrings.experimentalCallSwarm
|
||||||
|
fontPointSize: JamiTheme.settingsFontSize
|
||||||
|
tooltipText: JamiStrings.experimentalCallSwarmTooltip
|
||||||
|
onSwitchToggled: {
|
||||||
|
UtilsAdapter.setAppValue(Settings.Key.EnableExperimentalSwarm, checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -371,6 +371,8 @@ UtilsAdapter::setAppValue(const Settings::Key key, const QVariant& value)
|
||||||
settingsManager_->loadTranslations();
|
settingsManager_->loadTranslations();
|
||||||
else if (key == Settings::Key::BaseZoom)
|
else if (key == Settings::Key::BaseZoom)
|
||||||
Q_EMIT changeFontSize();
|
Q_EMIT changeFontSize();
|
||||||
|
else if (key == Settings::Key::EnableExperimentalSwarm)
|
||||||
|
Q_EMIT showExperimentalCallSwarm();
|
||||||
else if (key == Settings::Key::ShowChatviewHorizontally)
|
else if (key == Settings::Key::ShowChatviewHorizontally)
|
||||||
Q_EMIT chatviewPositionChanged();
|
Q_EMIT chatviewPositionChanged();
|
||||||
else if (key == Settings::Key::AppTheme)
|
else if (key == Settings::Key::AppTheme)
|
||||||
|
|
|
@ -126,6 +126,7 @@ Q_SIGNALS:
|
||||||
void changeFontSize();
|
void changeFontSize();
|
||||||
void chatviewPositionChanged();
|
void chatviewPositionChanged();
|
||||||
void appThemeChanged();
|
void appThemeChanged();
|
||||||
|
void showExperimentalCallSwarm();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QClipboard* clipboard_;
|
QClipboard* clipboard_;
|
||||||
|
|
|
@ -71,6 +71,9 @@ struct Info
|
||||||
QString uid = "";
|
QString uid = "";
|
||||||
QString accountId;
|
QString accountId;
|
||||||
QVector<member::Member> participants;
|
QVector<member::Member> participants;
|
||||||
|
VectorMapStringString activeCalls;
|
||||||
|
VectorMapStringString ignoredActiveCalls;
|
||||||
|
|
||||||
QString callId;
|
QString callId;
|
||||||
QString confId;
|
QString confId;
|
||||||
std::unique_ptr<MessageListModel> interactions;
|
std::unique_ptr<MessageListModel> interactions;
|
||||||
|
@ -84,6 +87,18 @@ struct Info
|
||||||
MapStringString infos {};
|
MapStringString infos {};
|
||||||
MapStringString preferences {};
|
MapStringString preferences {};
|
||||||
|
|
||||||
|
int indexOfActiveCall(const MapStringString& commit)
|
||||||
|
{
|
||||||
|
for (auto idx = 0; idx != activeCalls.size(); ++idx) {
|
||||||
|
const auto& call = activeCalls[idx];
|
||||||
|
if (call["id"] == commit["confId"] && call["uri"] == commit["uri"]
|
||||||
|
&& call["device"] == commit["device"]) {
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
QString getCallId() const
|
QString getCallId() const
|
||||||
{
|
{
|
||||||
return confId.isEmpty() ? callId : confId;
|
return confId.isEmpty() ? callId : confId;
|
||||||
|
|
|
@ -210,6 +210,11 @@ public:
|
||||||
* @param uid of the conversation
|
* @param uid of the conversation
|
||||||
*/
|
*/
|
||||||
void placeAudioOnlyCall(const QString& uid);
|
void placeAudioOnlyCall(const QString& uid);
|
||||||
|
void joinCall(const QString& uid,
|
||||||
|
const QString& confId,
|
||||||
|
const QString& uri,
|
||||||
|
const QString& deviceId,
|
||||||
|
bool isAudioOnly);
|
||||||
/**
|
/**
|
||||||
* Send a message to the conversation
|
* Send a message to the conversation
|
||||||
* @param uid of the conversation
|
* @param uid of the conversation
|
||||||
|
@ -378,6 +383,17 @@ public:
|
||||||
* @param conversationId
|
* @param conversationId
|
||||||
*/
|
*/
|
||||||
void popFrontError(const QString& conversationId);
|
void popFrontError(const QString& conversationId);
|
||||||
|
/**
|
||||||
|
* Ignore an active call
|
||||||
|
* @param convId
|
||||||
|
* @param id
|
||||||
|
* @param uri
|
||||||
|
* @param device
|
||||||
|
*/
|
||||||
|
void ignoreActiveCall(const QString& convId,
|
||||||
|
const QString& id,
|
||||||
|
const QString& uri,
|
||||||
|
const QString& device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return if conversations requests exists.
|
* @return if conversations requests exists.
|
||||||
|
@ -432,15 +448,6 @@ Q_SIGNALS:
|
||||||
void newInteraction(const QString& uid,
|
void newInteraction(const QString& uid,
|
||||||
QString& interactionId,
|
QString& interactionId,
|
||||||
const interaction::Info& interactionInfo) const;
|
const interaction::Info& interactionInfo) const;
|
||||||
/**
|
|
||||||
* Emitted when an interaction got a new status
|
|
||||||
* @param convUid conversation which owns the interaction
|
|
||||||
* @param interactionId
|
|
||||||
* @param msg
|
|
||||||
*/
|
|
||||||
void interactionStatusUpdated(const QString& convUid,
|
|
||||||
const QString& interactionId,
|
|
||||||
const api::interaction::Info& msg) const;
|
|
||||||
/**
|
/**
|
||||||
* Emitted when an interaction got removed from the conversation
|
* Emitted when an interaction got removed from the conversation
|
||||||
* @param convUid conversation which owns the interaction
|
* @param convUid conversation which owns the interaction
|
||||||
|
@ -546,6 +553,11 @@ Q_SIGNALS:
|
||||||
*/
|
*/
|
||||||
void newMessagesAvailable(const QString& accountId, const QString& conversationId) const;
|
void newMessagesAvailable(const QString& accountId, const QString& conversationId) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emitted whenever conversation's calls changed
|
||||||
|
*/
|
||||||
|
void activeCallsChanged(const QString& accountId, const QString& conversationId) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when creation of conversation started, finished with success or finisfed with error
|
* Emitted when creation of conversation started, finished with success or finisfed with error
|
||||||
* @param accountId account id
|
* @param accountId account id
|
||||||
|
@ -598,6 +610,11 @@ Q_SIGNALS:
|
||||||
void messagesFoundProcessed(const QString& accountId,
|
void messagesFoundProcessed(const QString& accountId,
|
||||||
const VectorMapStringString& messageIds,
|
const VectorMapStringString& messageIds,
|
||||||
const QVector<interaction::Info>& messageInformations) const;
|
const QVector<interaction::Info>& messageInformations) const;
|
||||||
|
/**
|
||||||
|
* Emitted once a conversation needs somebody to host the call
|
||||||
|
* @param callId
|
||||||
|
*/
|
||||||
|
void needsHost(const QString& conversationId) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ConversationModelPimpl> pimpl_;
|
std::unique_ptr<ConversationModelPimpl> pimpl_;
|
||||||
|
|
|
@ -272,6 +272,7 @@ struct Info
|
||||||
QString authorUri;
|
QString authorUri;
|
||||||
QString body;
|
QString body;
|
||||||
QString parentId = "";
|
QString parentId = "";
|
||||||
|
QString confId;
|
||||||
std::time_t timestamp = 0;
|
std::time_t timestamp = 0;
|
||||||
std::time_t duration = 0;
|
std::time_t duration = 0;
|
||||||
Type type = Type::INVALID;
|
Type type = Type::INVALID;
|
||||||
|
@ -318,6 +319,8 @@ struct Info
|
||||||
body = QObject::tr("Swarm created");
|
body = QObject::tr("Swarm created");
|
||||||
} else if (type == Type::CALL) {
|
} else if (type == Type::CALL) {
|
||||||
duration = message["duration"].toInt() / 1000;
|
duration = message["duration"].toInt() / 1000;
|
||||||
|
if (message.contains("confId"))
|
||||||
|
confId = message["confId"];
|
||||||
}
|
}
|
||||||
commit = message;
|
commit = message;
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@ getFormattedCallDuration(const std::time_t duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
getCallInteractionString(const QString& authorUri, const std::time_t& duration)
|
getCallInteractionStringNonSwarm(const QString& authorUri, const std::time_t& duration)
|
||||||
{
|
{
|
||||||
if (duration < 0) {
|
if (duration < 0) {
|
||||||
if (authorUri.isEmpty()) {
|
if (authorUri.isEmpty()) {
|
||||||
|
@ -190,6 +190,17 @@ getCallInteractionString(const QString& authorUri, const std::time_t& duration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
getCallInteractionString(const api::interaction::Info& info)
|
||||||
|
{
|
||||||
|
if (!info.confId.isEmpty()) {
|
||||||
|
if (info.duration <= 0) {
|
||||||
|
return QObject::tr("Join call");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getCallInteractionStringNonSwarm(info.authorUri, info.duration);
|
||||||
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
getContactInteractionString(const QString& authorUri, const api::interaction::Status& status)
|
getContactInteractionString(const QString& authorUri, const api::interaction::Status& status)
|
||||||
{
|
{
|
||||||
|
@ -510,7 +521,7 @@ getHistory(Database& db, api::conversation::Info& conversation)
|
||||||
: std::stoi(durationString.toStdString());
|
: std::stoi(durationString.toStdString());
|
||||||
auto status = api::interaction::to_status(payloads[i + 5]);
|
auto status = api::interaction::to_status(payloads[i + 5]);
|
||||||
if (type == api::interaction::Type::CALL) {
|
if (type == api::interaction::Type::CALL) {
|
||||||
body = getCallInteractionString(payloads[i + 1], duration);
|
body = getCallInteractionStringNonSwarm(payloads[i + 1], duration);
|
||||||
} else if (type == api::interaction::Type::CONTACT) {
|
} else if (type == api::interaction::Type::CONTACT) {
|
||||||
body = getContactInteractionString(payloads[i + 1], status);
|
body = getContactInteractionString(payloads[i + 1], status);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,11 +56,11 @@ QString prepareUri(const QString& uri, api::profile::Type type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a formatted string for a call interaction's body
|
* Get a formatted string for a call interaction's body
|
||||||
* @param author_uri
|
* @param info
|
||||||
* @param duration of the call
|
|
||||||
* @return the formatted and translated call message string
|
* @return the formatted and translated call message string
|
||||||
*/
|
*/
|
||||||
QString getCallInteractionString(const QString& authorUri, const std::time_t& duration);
|
QString getCallInteractionString(const api::interaction::Info& info);
|
||||||
|
QString getCallInteractionStringNonSwarm(const QString& authorUri, const std::time_t& duration);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a formatted string for a contact interaction's body
|
* Get a formatted string for a contact interaction's body
|
||||||
|
|
|
@ -128,6 +128,12 @@ CallbacksHandler::CallbacksHandler(const Lrc& parent)
|
||||||
&CallbacksHandler::slotAccountMessageStatusChanged,
|
&CallbacksHandler::slotAccountMessageStatusChanged,
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(&ConfigurationManager::instance(),
|
||||||
|
&ConfigurationManagerInterface::needsHost,
|
||||||
|
this,
|
||||||
|
&CallbacksHandler::slotNeedsHost,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
|
||||||
connect(&ConfigurationManager::instance(),
|
connect(&ConfigurationManager::instance(),
|
||||||
&ConfigurationManagerInterface::accountDetailsChanged,
|
&ConfigurationManagerInterface::accountDetailsChanged,
|
||||||
this,
|
this,
|
||||||
|
@ -349,6 +355,11 @@ CallbacksHandler::CallbacksHandler(const Lrc& parent)
|
||||||
this,
|
this,
|
||||||
&CallbacksHandler::slotOnConversationError,
|
&CallbacksHandler::slotOnConversationError,
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
|
connect(&ConfigurationManager::instance(),
|
||||||
|
&ConfigurationManagerInterface::activeCallsChanged,
|
||||||
|
this,
|
||||||
|
&CallbacksHandler::slotActiveCallsChanged,
|
||||||
|
Qt::QueuedConnection);
|
||||||
connect(&ConfigurationManager::instance(),
|
connect(&ConfigurationManager::instance(),
|
||||||
&ConfigurationManagerInterface::conversationPreferencesUpdated,
|
&ConfigurationManagerInterface::conversationPreferencesUpdated,
|
||||||
this,
|
this,
|
||||||
|
@ -576,6 +587,7 @@ CallbacksHandler::slotConferenceChanged(const QString& accountId,
|
||||||
const QString& callId,
|
const QString& callId,
|
||||||
const QString& state)
|
const QString& state)
|
||||||
{
|
{
|
||||||
|
Q_EMIT conferenceChanged(accountId, callId, state);
|
||||||
slotCallStateChanged(accountId, callId, state, 0);
|
slotCallStateChanged(accountId, callId, state, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,6 +607,12 @@ CallbacksHandler::slotAccountMessageStatusChanged(const QString& accountId,
|
||||||
Q_EMIT accountMessageStatusChanged(accountId, conversationId, peer, messageId, status);
|
Q_EMIT accountMessageStatusChanged(accountId, conversationId, peer, messageId, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CallbacksHandler::slotNeedsHost(const QString& accountId, const QString& conversationId)
|
||||||
|
{
|
||||||
|
Q_EMIT needsHost(accountId, conversationId);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CallbacksHandler::slotDataTransferEvent(const QString& accountId,
|
CallbacksHandler::slotDataTransferEvent(const QString& accountId,
|
||||||
const QString& conversationId,
|
const QString& conversationId,
|
||||||
|
@ -823,6 +841,14 @@ CallbacksHandler::slotOnConversationError(const QString& accountId,
|
||||||
Q_EMIT conversationError(accountId, conversationId, code, what);
|
Q_EMIT conversationError(accountId, conversationId, code, what);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CallbacksHandler::slotActiveCallsChanged(const QString& accountId,
|
||||||
|
const QString& conversationId,
|
||||||
|
const VectorMapStringString& activeCalls)
|
||||||
|
{
|
||||||
|
Q_EMIT activeCallsChanged(accountId, conversationId, activeCalls);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CallbacksHandler::slotConversationPreferencesUpdated(const QString& accountId,
|
CallbacksHandler::slotConversationPreferencesUpdated(const QString& accountId,
|
||||||
const QString& conversationId,
|
const QString& conversationId,
|
||||||
|
|
|
@ -195,12 +195,19 @@ Q_SIGNALS:
|
||||||
* @param callId of the conference
|
* @param callId of the conference
|
||||||
*/
|
*/
|
||||||
void conferenceCreated(const QString& accountId, const QString& callId);
|
void conferenceCreated(const QString& accountId, const QString& callId);
|
||||||
|
void conferenceChanged(const QString& accountId, const QString& confId, const QString& state);
|
||||||
/**
|
/**
|
||||||
* Connect this signal to know when a conference is removed
|
* Connect this signal to know when a conference is removed
|
||||||
* @param accountId
|
* @param accountId
|
||||||
* @param callId of the conference
|
* @param callId of the conference
|
||||||
*/
|
*/
|
||||||
void conferenceRemoved(const QString& accountId, const QString& callId);
|
void conferenceRemoved(const QString& accountId, const QString& callId);
|
||||||
|
/**
|
||||||
|
* Connect this signal to know if a conversation needs an host.
|
||||||
|
* @param accountId, account linked
|
||||||
|
* @param conversationId id of the conversation
|
||||||
|
*/
|
||||||
|
void needsHost(const QString& accountId, const QString& conversationId);
|
||||||
/**
|
/**
|
||||||
* Connect this signal to know when a message sent get a new status
|
* Connect this signal to know when a message sent get a new status
|
||||||
* @param accountId, account linked
|
* @param accountId, account linked
|
||||||
|
@ -374,6 +381,9 @@ Q_SIGNALS:
|
||||||
const QString& conversationId,
|
const QString& conversationId,
|
||||||
int code,
|
int code,
|
||||||
const QString& what);
|
const QString& what);
|
||||||
|
void activeCallsChanged(const QString& accountId,
|
||||||
|
const QString& conversationId,
|
||||||
|
const VectorMapStringString& activeCalls);
|
||||||
void conversationPreferencesUpdated(const QString& accountId,
|
void conversationPreferencesUpdated(const QString& accountId,
|
||||||
const QString& conversationId,
|
const QString& conversationId,
|
||||||
const MapStringString& preferences);
|
const MapStringString& preferences);
|
||||||
|
@ -549,6 +559,12 @@ private Q_SLOTS:
|
||||||
const QString& peer,
|
const QString& peer,
|
||||||
const QString& messageId,
|
const QString& messageId,
|
||||||
int status);
|
int status);
|
||||||
|
/**
|
||||||
|
* Emit needsHost
|
||||||
|
* @param accountId, account linked
|
||||||
|
* @param conversationId id of the conversation
|
||||||
|
*/
|
||||||
|
void slotNeedsHost(const QString& accountId, const QString& conversationId);
|
||||||
|
|
||||||
void slotDataTransferEvent(const QString& accountId,
|
void slotDataTransferEvent(const QString& accountId,
|
||||||
const QString& conversationId,
|
const QString& conversationId,
|
||||||
|
@ -699,6 +715,9 @@ private Q_SLOTS:
|
||||||
const QString& conversationId,
|
const QString& conversationId,
|
||||||
int code,
|
int code,
|
||||||
const QString& what);
|
const QString& what);
|
||||||
|
void slotActiveCallsChanged(const QString& accountId,
|
||||||
|
const QString& conversationId,
|
||||||
|
const VectorMapStringString& activeCalls);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const api::Lrc& parent;
|
const api::Lrc& parent;
|
||||||
|
|
|
@ -239,6 +239,9 @@ public Q_SLOTS:
|
||||||
* @param callId
|
* @param callId
|
||||||
*/
|
*/
|
||||||
void slotConferenceCreated(const QString& accountId, const QString& callId);
|
void slotConferenceCreated(const QString& accountId, const QString& callId);
|
||||||
|
void slotConferenceChanged(const QString& accountId,
|
||||||
|
const QString& callId,
|
||||||
|
const QString& state);
|
||||||
/**
|
/**
|
||||||
* Listen from CallbacksHandler when a voice mail notice is incoming
|
* Listen from CallbacksHandler when a voice mail notice is incoming
|
||||||
* @param accountId
|
* @param accountId
|
||||||
|
@ -409,6 +412,23 @@ CallModel::getAdvancedInformation() const
|
||||||
return pimpl_->callAdvancedInformation();
|
return pimpl_->callAdvancedInformation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CallModel::emplaceConversationConference(const QString& confId)
|
||||||
|
{
|
||||||
|
if (hasCall(confId))
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto callInfo = std::make_shared<call::Info>();
|
||||||
|
callInfo->id = confId;
|
||||||
|
callInfo->isOutgoing = false;
|
||||||
|
callInfo->status = call::Status::SEARCHING;
|
||||||
|
callInfo->type = call::Type::CONFERENCE;
|
||||||
|
callInfo->isAudioOnly = false;
|
||||||
|
callInfo->videoMuted = false;
|
||||||
|
callInfo->mediaList = {};
|
||||||
|
pimpl_->calls.emplace(confId, std::move(callInfo));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CallModel::muteMedia(const QString& callId, const QString& label, bool mute)
|
CallModel::muteMedia(const QString& callId, const QString& label, bool mute)
|
||||||
{
|
{
|
||||||
|
@ -940,6 +960,10 @@ CallModelPimpl::CallModelPimpl(const CallModel& linked,
|
||||||
&CallbacksHandler::conferenceCreated,
|
&CallbacksHandler::conferenceCreated,
|
||||||
this,
|
this,
|
||||||
&CallModelPimpl::slotConferenceCreated);
|
&CallModelPimpl::slotConferenceCreated);
|
||||||
|
connect(&callbacksHandler,
|
||||||
|
&CallbacksHandler::conferenceChanged,
|
||||||
|
this,
|
||||||
|
&CallModelPimpl::slotConferenceChanged);
|
||||||
connect(&callbacksHandler,
|
connect(&callbacksHandler,
|
||||||
&CallbacksHandler::voiceMailNotify,
|
&CallbacksHandler::voiceMailNotify,
|
||||||
this,
|
this,
|
||||||
|
@ -1566,10 +1590,14 @@ CallModelPimpl::slotOnConferenceInfosUpdated(const QString& confId,
|
||||||
QStringList callList = CallManager::instance().getParticipantList(linked.owner.id, confId);
|
QStringList callList = CallManager::instance().getParticipantList(linked.owner.id, confId);
|
||||||
Q_FOREACH (const auto& call, callList) {
|
Q_FOREACH (const auto& call, callList) {
|
||||||
Q_EMIT linked.callAddedToConference(call, confId);
|
Q_EMIT linked.callAddedToConference(call, confId);
|
||||||
|
if (calls.find(call) == calls.end()) {
|
||||||
|
qWarning() << "Call not found";
|
||||||
|
} else {
|
||||||
calls[call]->videoMuted = it->second->videoMuted;
|
calls[call]->videoMuted = it->second->videoMuted;
|
||||||
calls[call]->audioMuted = it->second->audioMuted;
|
calls[call]->audioMuted = it->second->audioMuted;
|
||||||
Q_EMIT linked.callInfosChanged(linked.owner.id, call);
|
Q_EMIT linked.callInfosChanged(linked.owner.id, call);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Q_EMIT linked.callInfosChanged(linked.owner.id, confId);
|
Q_EMIT linked.callInfosChanged(linked.owner.id, confId);
|
||||||
Q_EMIT linked.onParticipantsChanged(confId);
|
Q_EMIT linked.onParticipantsChanged(confId);
|
||||||
}
|
}
|
||||||
|
@ -1585,14 +1613,7 @@ CallModelPimpl::slotConferenceCreated(const QString& accountId, const QString& c
|
||||||
{
|
{
|
||||||
if (accountId != linked.owner.id)
|
if (accountId != linked.owner.id)
|
||||||
return;
|
return;
|
||||||
// Detect if conference is created for this account
|
|
||||||
QStringList callList = CallManager::instance().getParticipantList(linked.owner.id, confId);
|
QStringList callList = CallManager::instance().getParticipantList(linked.owner.id, confId);
|
||||||
auto hasConference = false;
|
|
||||||
Q_FOREACH (const auto& call, callList) {
|
|
||||||
hasConference |= linked.hasCall(call);
|
|
||||||
}
|
|
||||||
if (!hasConference)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto callInfo = std::make_shared<call::Info>();
|
auto callInfo = std::make_shared<call::Info>();
|
||||||
callInfo->id = confId;
|
callInfo->id = confId;
|
||||||
|
@ -1625,6 +1646,20 @@ CallModelPimpl::slotConferenceCreated(const QString& accountId, const QString& c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CallModelPimpl::slotConferenceChanged(const QString& accountId,
|
||||||
|
const QString& confId,
|
||||||
|
const QString& state)
|
||||||
|
{
|
||||||
|
if (accountId != linked.owner.id)
|
||||||
|
return;
|
||||||
|
// Detect if conference is created for this account
|
||||||
|
QStringList callList = CallManager::instance().getParticipantList(linked.owner.id, confId);
|
||||||
|
Q_FOREACH (const auto& call, callList) {
|
||||||
|
Q_EMIT linked.callAddedToConference(call, confId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CallModelPimpl::sendProfile(const QString& callId)
|
CallModelPimpl::sendProfile(const QString& callId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -193,10 +193,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* Handle data transfer progression
|
* Handle data transfer progression
|
||||||
*/
|
*/
|
||||||
void updateTransferProgress(QTimer* timer,
|
void updateTransferProgress(QTimer* timer, int conversationIdx, const QString& interactionId);
|
||||||
const QString& conversation,
|
|
||||||
int conversationIdx,
|
|
||||||
const QString& interactionId);
|
|
||||||
|
|
||||||
bool usefulDataFromDataTransfer(const QString& fileId,
|
bool usefulDataFromDataTransfer(const QString& fileId,
|
||||||
const datatransfer::Info& info,
|
const datatransfer::Info& info,
|
||||||
|
@ -385,6 +382,9 @@ public Q_SLOTS:
|
||||||
const QString& conversationId,
|
const QString& conversationId,
|
||||||
int code,
|
int code,
|
||||||
const QString& what);
|
const QString& what);
|
||||||
|
void slotActiveCallsChanged(const QString& accountId,
|
||||||
|
const QString& conversationId,
|
||||||
|
const VectorMapStringString& activeCalls);
|
||||||
void slotConversationReady(const QString& accountId, const QString& conversationId);
|
void slotConversationReady(const QString& accountId, const QString& conversationId);
|
||||||
void slotConversationRemoved(const QString& accountId, const QString& conversationId);
|
void slotConversationRemoved(const QString& accountId, const QString& conversationId);
|
||||||
void slotConversationPreferencesUpdated(const QString& accountId,
|
void slotConversationPreferencesUpdated(const QString& accountId,
|
||||||
|
@ -853,6 +853,30 @@ ConversationModel::deleteObsoleteHistory(int days)
|
||||||
storage::deleteObsoleteHistory(pimpl_->db, date);
|
storage::deleteObsoleteHistory(pimpl_->db, date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConversationModel::joinCall(const QString& uid,
|
||||||
|
const QString& uri,
|
||||||
|
const QString& deviceId,
|
||||||
|
const QString& confId,
|
||||||
|
bool isAudioOnly)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
auto& conversation = pimpl_->getConversationForUid(uid, true).get();
|
||||||
|
if (!conversation.callId.isEmpty()) {
|
||||||
|
qWarning() << "Already in a call for swarm:" + uid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
conversation.callId = owner.callModel->createCall("rdv:" + uid + "/" + uri + "/" + deviceId
|
||||||
|
+ "/" + confId,
|
||||||
|
isAudioOnly);
|
||||||
|
// Update interaction status
|
||||||
|
pimpl_->invalidateModel();
|
||||||
|
emit selectConversation(uid);
|
||||||
|
emit conversationUpdated(uid);
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ConversationModelPimpl::placeCall(const QString& uid, bool isAudioOnly)
|
ConversationModelPimpl::placeCall(const QString& uid, bool isAudioOnly)
|
||||||
{
|
{
|
||||||
|
@ -864,6 +888,19 @@ ConversationModelPimpl::placeCall(const QString& uid, bool isAudioOnly)
|
||||||
<< "ConversationModel::placeCall can't call a conversation without participant";
|
<< "ConversationModel::placeCall can't call a conversation without participant";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!conversation.isCoreDialog() && conversation.isSwarm()) {
|
||||||
|
qDebug() << "Start call for swarm:" + uid;
|
||||||
|
conversation.callId = linked.owner.callModel->createCall("swarm:" + uid, isAudioOnly);
|
||||||
|
|
||||||
|
// Update interaction status
|
||||||
|
invalidateModel();
|
||||||
|
emit linked.selectConversation(conversation.uid);
|
||||||
|
emit linked.conversationUpdated(conversation.uid);
|
||||||
|
Q_EMIT linked.dataChanged(indexOf(conversation.uid));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto& peers = peersForConversation(conversation);
|
auto& peers = peersForConversation(conversation);
|
||||||
// there is no calls in group with more than 2 participants
|
// there is no calls in group with more than 2 participants
|
||||||
if (peers.size() != 1) {
|
if (peers.size() != 1) {
|
||||||
|
@ -1028,6 +1065,25 @@ ConversationModel::popFrontError(const QString& conversationId)
|
||||||
Q_EMIT onConversationErrorsUpdated(conversationId);
|
Q_EMIT onConversationErrorsUpdated(conversationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConversationModel::ignoreActiveCall(const QString& conversationId,
|
||||||
|
const QString& id,
|
||||||
|
const QString& uri,
|
||||||
|
const QString& device)
|
||||||
|
{
|
||||||
|
auto conversationOpt = getConversationForUid(conversationId);
|
||||||
|
if (!conversationOpt.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto& conversation = conversationOpt->get();
|
||||||
|
MapStringString mapCall;
|
||||||
|
mapCall["id"] = id;
|
||||||
|
mapCall["uri"] = uri;
|
||||||
|
mapCall["device"] = device;
|
||||||
|
conversation.ignoredActiveCalls.push_back(mapCall);
|
||||||
|
Q_EMIT activeCallsChanged(owner.id, conversationId);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ConversationModel::setConversationPreferences(const QString& conversationId,
|
ConversationModel::setConversationPreferences(const QString& conversationId,
|
||||||
const MapStringString prefs)
|
const MapStringString prefs)
|
||||||
|
@ -1835,6 +1891,9 @@ ConversationModelPimpl::ConversationModelPimpl(const ConversationModel& linked,
|
||||||
&ConfigurationManagerInterface::composingStatusChanged,
|
&ConfigurationManagerInterface::composingStatusChanged,
|
||||||
this,
|
this,
|
||||||
&ConversationModelPimpl::slotComposingStatusChanged);
|
&ConversationModelPimpl::slotComposingStatusChanged);
|
||||||
|
connect(&callbacksHandler, &CallbacksHandler::needsHost, this, [&](auto, auto convId) {
|
||||||
|
emit linked.needsHost(convId);
|
||||||
|
});
|
||||||
|
|
||||||
// data transfer
|
// data transfer
|
||||||
connect(&*linked.owner.contactModel,
|
connect(&*linked.owner.contactModel,
|
||||||
|
@ -1918,6 +1977,10 @@ ConversationModelPimpl::ConversationModelPimpl(const ConversationModel& linked,
|
||||||
&CallbacksHandler::conversationPreferencesUpdated,
|
&CallbacksHandler::conversationPreferencesUpdated,
|
||||||
this,
|
this,
|
||||||
&ConversationModelPimpl::slotConversationPreferencesUpdated);
|
&ConversationModelPimpl::slotConversationPreferencesUpdated);
|
||||||
|
connect(&callbacksHandler,
|
||||||
|
&CallbacksHandler::activeCallsChanged,
|
||||||
|
this,
|
||||||
|
&ConversationModelPimpl::slotActiveCallsChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConversationModelPimpl::~ConversationModelPimpl()
|
ConversationModelPimpl::~ConversationModelPimpl()
|
||||||
|
@ -2066,6 +2129,10 @@ ConversationModelPimpl::~ConversationModelPimpl()
|
||||||
&CallbacksHandler::conversationError,
|
&CallbacksHandler::conversationError,
|
||||||
this,
|
this,
|
||||||
&ConversationModelPimpl::slotOnConversationError);
|
&ConversationModelPimpl::slotOnConversationError);
|
||||||
|
disconnect(&callbacksHandler,
|
||||||
|
&CallbacksHandler::activeCallsChanged,
|
||||||
|
this,
|
||||||
|
&ConversationModelPimpl::slotActiveCallsChanged);
|
||||||
disconnect(&callbacksHandler,
|
disconnect(&callbacksHandler,
|
||||||
&CallbacksHandler::conversationPreferencesUpdated,
|
&CallbacksHandler::conversationPreferencesUpdated,
|
||||||
this,
|
this,
|
||||||
|
@ -2383,7 +2450,7 @@ ConversationModelPimpl::slotConversationLoaded(uint32_t requestId,
|
||||||
linked.owner.dataTransferModel->registerTransferId(fileId, msgId);
|
linked.owner.dataTransferModel->registerTransferId(fileId, msgId);
|
||||||
downloadFile = (bytesProgress == 0);
|
downloadFile = (bytesProgress == 0);
|
||||||
} else if (msg.type == interaction::Type::CALL) {
|
} else if (msg.type == interaction::Type::CALL) {
|
||||||
msg.body = storage::getCallInteractionString(msg.authorUri, msg.duration);
|
msg.body = storage::getCallInteractionString(msg);
|
||||||
} else if (msg.type == interaction::Type::CONTACT) {
|
} else if (msg.type == interaction::Type::CONTACT) {
|
||||||
auto bestName = msg.authorUri == linked.owner.profileInfo.uri
|
auto bestName = msg.authorUri == linked.owner.profileInfo.uri
|
||||||
? linked.owner.accountModel->bestNameForAccount(linked.owner.id)
|
? linked.owner.accountModel->bestNameForAccount(linked.owner.id)
|
||||||
|
@ -2498,6 +2565,8 @@ ConversationModelPimpl::slotMessageReceived(const QString& accountId,
|
||||||
api::datatransfer::Info info;
|
api::datatransfer::Info info;
|
||||||
QString fileId;
|
QString fileId;
|
||||||
|
|
||||||
|
auto updateUnread = false;
|
||||||
|
|
||||||
if (msg.type == interaction::Type::DATA_TRANSFER) {
|
if (msg.type == interaction::Type::DATA_TRANSFER) {
|
||||||
// save data transfer interaction to db and assosiate daemon id with interaction id,
|
// save data transfer interaction to db and assosiate daemon id with interaction id,
|
||||||
// conversation id and db id
|
// conversation id and db id
|
||||||
|
@ -2520,8 +2589,14 @@ ConversationModelPimpl::slotMessageReceived(const QString& accountId,
|
||||||
: bytesProgress == totalSize ? interaction::Status::TRANSFER_FINISHED
|
: bytesProgress == totalSize ? interaction::Status::TRANSFER_FINISHED
|
||||||
: interaction::Status::TRANSFER_ONGOING;
|
: interaction::Status::TRANSFER_ONGOING;
|
||||||
linked.owner.dataTransferModel->registerTransferId(fileId, msgId);
|
linked.owner.dataTransferModel->registerTransferId(fileId, msgId);
|
||||||
|
if (msg.authorUri != linked.owner.profileInfo.uri) {
|
||||||
|
updateUnread = true;
|
||||||
|
}
|
||||||
} else if (msg.type == interaction::Type::CALL) {
|
} else if (msg.type == interaction::Type::CALL) {
|
||||||
msg.body = storage::getCallInteractionString(msg.authorUri, msg.duration);
|
// If we're a call in a swarm
|
||||||
|
if (msg.authorUri != linked.owner.profileInfo.uri)
|
||||||
|
updateUnread = true;
|
||||||
|
msg.body = storage::getCallInteractionString(msg);
|
||||||
} else if (msg.type == interaction::Type::CONTACT) {
|
} else if (msg.type == interaction::Type::CONTACT) {
|
||||||
auto bestName = msg.authorUri == linked.owner.profileInfo.uri
|
auto bestName = msg.authorUri == linked.owner.profileInfo.uri
|
||||||
? linked.owner.accountModel->bestNameForAccount(linked.owner.id)
|
? linked.owner.accountModel->bestNameForAccount(linked.owner.id)
|
||||||
|
@ -2529,16 +2604,24 @@ ConversationModelPimpl::slotMessageReceived(const QString& accountId,
|
||||||
msg.body = interaction::getContactInteractionString(bestName,
|
msg.body = interaction::getContactInteractionString(bestName,
|
||||||
interaction::to_action(
|
interaction::to_action(
|
||||||
message["action"]));
|
message["action"]));
|
||||||
} else if (msg.type == interaction::Type::TEXT
|
if (msg.authorUri != linked.owner.profileInfo.uri) {
|
||||||
&& msg.authorUri != linked.owner.profileInfo.uri) {
|
updateUnread = true;
|
||||||
conversation.unreadMessages++;
|
}
|
||||||
|
} else if (msg.type == interaction::Type::TEXT) {
|
||||||
|
if (msg.authorUri != linked.owner.profileInfo.uri) {
|
||||||
|
updateUnread = true;
|
||||||
|
}
|
||||||
} else if (msg.type == interaction::Type::EDITED) {
|
} else if (msg.type == interaction::Type::EDITED) {
|
||||||
conversation.interactions->addEdition(msgId, msg, true);
|
conversation.interactions->addEdition(msgId, msg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!insertSwarmInteraction(msgId, msg, conversation, false)) {
|
if (!insertSwarmInteraction(msgId, msg, conversation, false)) {
|
||||||
// message already exists
|
// message already exists
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (updateUnread) {
|
||||||
|
conversation.unreadMessages++;
|
||||||
|
}
|
||||||
if (msg.type == interaction::Type::MERGE) {
|
if (msg.type == interaction::Type::MERGE) {
|
||||||
invalidateModel();
|
invalidateModel();
|
||||||
return;
|
return;
|
||||||
|
@ -2811,6 +2894,24 @@ ConversationModelPimpl::slotOnConversationError(const QString& accountId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConversationModelPimpl::slotActiveCallsChanged(const QString& accountId,
|
||||||
|
const QString& conversationId,
|
||||||
|
const VectorMapStringString& activeCalls)
|
||||||
|
{
|
||||||
|
if (accountId != linked.owner.id || indexOf(conversationId) < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
auto& conversation = getConversationForUid(conversationId).get();
|
||||||
|
conversation.activeCalls = activeCalls;
|
||||||
|
if (activeCalls.empty())
|
||||||
|
conversation.ignoredActiveCalls.clear();
|
||||||
|
Q_EMIT linked.activeCallsChanged(accountId, conversationId);
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ConversationModelPimpl::slotIncomingContactRequest(const QString& contactUri)
|
ConversationModelPimpl::slotIncomingContactRequest(const QString& contactUri)
|
||||||
{
|
{
|
||||||
|
@ -3074,6 +3175,9 @@ ConversationModelPimpl::addSwarmConversation(const QString& convId)
|
||||||
conversation.infos = details;
|
conversation.infos = details;
|
||||||
conversation.uid = convId;
|
conversation.uid = convId;
|
||||||
conversation.accountId = linked.owner.id;
|
conversation.accountId = linked.owner.id;
|
||||||
|
VectorMapStringString activeCalls = ConfigurationManager::instance()
|
||||||
|
.getActiveCalls(linked.owner.id, convId);
|
||||||
|
conversation.activeCalls = activeCalls;
|
||||||
QString lastRead;
|
QString lastRead;
|
||||||
VectorString membersLeft;
|
VectorString membersLeft;
|
||||||
for (auto& member : members) {
|
for (auto& member : members) {
|
||||||
|
@ -3387,12 +3491,13 @@ ConversationModelPimpl::addOrUpdateCallMessage(const QString& callId,
|
||||||
bool incoming,
|
bool incoming,
|
||||||
const std::time_t& duration)
|
const std::time_t& duration)
|
||||||
{
|
{
|
||||||
// do not save call interaction for swarm conversation
|
// Get conversation
|
||||||
try {
|
auto conv_it = std::find_if(conversations.begin(),
|
||||||
auto& conv = getConversationForPeerUri(from).get();
|
conversations.end(),
|
||||||
if (conv.isSwarm())
|
[&callId](const conversation::Info& conversation) {
|
||||||
return;
|
return conversation.callId == callId;
|
||||||
} catch (const std::exception&) {
|
});
|
||||||
|
if (conv_it == conversations.end()) {
|
||||||
// If we have no conversation with peer.
|
// If we have no conversation with peer.
|
||||||
try {
|
try {
|
||||||
auto contact = linked.owner.contactModel->getContact(from);
|
auto contact = linked.owner.contactModel->getContact(from);
|
||||||
|
@ -3401,18 +3506,18 @@ ConversationModelPimpl::addOrUpdateCallMessage(const QString& callId,
|
||||||
storage::beginConversationWithPeer(db, contact.profileInfo.uri);
|
storage::beginConversationWithPeer(db, contact.profileInfo.uri);
|
||||||
}
|
}
|
||||||
} catch (const std::exception&) {
|
} catch (const std::exception&) {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get conversation
|
|
||||||
auto conv_it = std::find_if(conversations.begin(),
|
|
||||||
conversations.end(),
|
|
||||||
[&callId](const conversation::Info& conversation) {
|
|
||||||
return conversation.callId == callId;
|
|
||||||
});
|
|
||||||
if (conv_it == conversations.end()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
auto& conv = getConversationForPeerUri(from).get();
|
||||||
|
conv.callId = callId;
|
||||||
|
} catch (...) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// do not save call interaction for swarm conversation
|
||||||
|
if (conv_it->isSwarm())
|
||||||
|
return;
|
||||||
auto uid = conv_it->uid;
|
auto uid = conv_it->uid;
|
||||||
auto uriString = incoming ? storage::prepareUri(from, linked.owner.profileInfo.type) : "";
|
auto uriString = incoming ? storage::prepareUri(from, linked.owner.profileInfo.type) : "";
|
||||||
auto msg = interaction::Info {uriString,
|
auto msg = interaction::Info {uriString,
|
||||||
|
@ -3425,7 +3530,7 @@ ConversationModelPimpl::addOrUpdateCallMessage(const QString& callId,
|
||||||
// update the db
|
// update the db
|
||||||
auto msgId = storage::addOrUpdateMessage(db, conv_it->uid, msg, callId);
|
auto msgId = storage::addOrUpdateMessage(db, conv_it->uid, msg, callId);
|
||||||
// now set the formatted call message string in memory only
|
// now set the formatted call message string in memory only
|
||||||
msg.body = storage::getCallInteractionString(uriString, duration);
|
msg.body = storage::getCallInteractionString(msg);
|
||||||
bool newInteraction = false;
|
bool newInteraction = false;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(interactionsLocks[conv_it->uid]);
|
std::lock_guard<std::mutex> lk(interactionsLocks[conv_it->uid]);
|
||||||
|
@ -3562,6 +3667,7 @@ ConversationModelPimpl::slotCallAddedToConference(const QString& callId, const Q
|
||||||
.getConferenceDetails(linked.owner.id, confId);
|
.getConferenceDetails(linked.owner.id, confId);
|
||||||
if (confDetails["STATE"] == "ACTIVE_ATTACHED")
|
if (confDetails["STATE"] == "ACTIVE_ATTACHED")
|
||||||
Q_EMIT linked.selectConversation(conversation.uid);
|
Q_EMIT linked.selectConversation(conversation.uid);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3693,7 +3799,7 @@ ConversationModelPimpl::slotUpdateInteractionStatus(const QString& accountId,
|
||||||
if (peerId != linked.owner.profileInfo.uri)
|
if (peerId != linked.owner.profileInfo.uri)
|
||||||
conversation.interactions->setRead(peerId, messageId);
|
conversation.interactions->setRead(peerId, messageId);
|
||||||
else {
|
else {
|
||||||
// Here, this means that the daemon synched the displayed message
|
// Here, this means that the daemon synced the displayed message
|
||||||
// so, compute the number of unread messages.
|
// so, compute the number of unread messages.
|
||||||
conversation.unreadMessages = ConfigurationManager::instance()
|
conversation.unreadMessages = ConfigurationManager::instance()
|
||||||
.countInteractions(linked.owner.id,
|
.countInteractions(linked.owner.id,
|
||||||
|
@ -4150,7 +4256,7 @@ ConversationModelPimpl::slotTransferStatusOngoing(const QString& fileId, datatra
|
||||||
auto conversationIdx = indexOf(conversationId);
|
auto conversationIdx = indexOf(conversationId);
|
||||||
auto* timer = new QTimer();
|
auto* timer = new QTimer();
|
||||||
connect(timer, &QTimer::timeout, [=] {
|
connect(timer, &QTimer::timeout, [=] {
|
||||||
updateTransferProgress(timer, conversationId, conversationIdx, interactionId);
|
updateTransferProgress(timer, conversationIdx, interactionId);
|
||||||
});
|
});
|
||||||
timer->start(1000);
|
timer->start(1000);
|
||||||
}
|
}
|
||||||
|
@ -4281,7 +4387,6 @@ ConversationModelPimpl::updateTransferStatus(const QString& fileId,
|
||||||
|
|
||||||
void
|
void
|
||||||
ConversationModelPimpl::updateTransferProgress(QTimer* timer,
|
ConversationModelPimpl::updateTransferProgress(QTimer* timer,
|
||||||
const QString&,
|
|
||||||
int conversationIdx,
|
int conversationIdx,
|
||||||
const QString& interactionId)
|
const QString& interactionId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -68,6 +68,20 @@ MessageListModel::find(const QString& msgId)
|
||||||
return interactions_.end();
|
return interactions_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iterator
|
||||||
|
MessageListModel::findActiveCall(const MapStringString& commit)
|
||||||
|
{
|
||||||
|
iterator it;
|
||||||
|
for (it = interactions_.begin(); it != interactions_.end(); ++it) {
|
||||||
|
const auto& itCommit = it->second.commit;
|
||||||
|
if (itCommit["confId"] == commit["confId"] && itCommit["uri"] == commit["uri"]
|
||||||
|
&& itCommit["device"] == commit["device"]) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return interactions_.end();
|
||||||
|
}
|
||||||
|
|
||||||
iterator
|
iterator
|
||||||
MessageListModel::erase(const iterator& it)
|
MessageListModel::erase(const iterator& it)
|
||||||
{
|
{
|
||||||
|
@ -403,6 +417,13 @@ MessageListModel::dataForItem(item_t item, int, int role) const
|
||||||
case Role::Timestamp:
|
case Role::Timestamp:
|
||||||
return QVariant::fromValue(item.second.timestamp);
|
return QVariant::fromValue(item.second.timestamp);
|
||||||
case Role::Duration:
|
case Role::Duration:
|
||||||
|
if (!item.second.commit.empty()) {
|
||||||
|
// For swarm, check the commit value
|
||||||
|
if (item.second.commit.find("duration") == item.second.commit.end())
|
||||||
|
return QVariant::fromValue(0);
|
||||||
|
else
|
||||||
|
return QVariant::fromValue(item.second.commit["duration"].toInt() / 1000);
|
||||||
|
}
|
||||||
return QVariant::fromValue(item.second.duration);
|
return QVariant::fromValue(item.second.duration);
|
||||||
case Role::Type:
|
case Role::Type:
|
||||||
return QVariant(static_cast<int>(item.second.type));
|
return QVariant(static_cast<int>(item.second.type));
|
||||||
|
@ -416,6 +437,10 @@ MessageListModel::dataForItem(item_t item, int, int role) const
|
||||||
return QVariant(item.second.linkified);
|
return QVariant(item.second.linkified);
|
||||||
case Role::ActionUri:
|
case Role::ActionUri:
|
||||||
return QVariant(item.second.commit["uri"]);
|
return QVariant(item.second.commit["uri"]);
|
||||||
|
case Role::ConfId:
|
||||||
|
return QVariant(item.second.commit["confId"]);
|
||||||
|
case Role::DeviceId:
|
||||||
|
return QVariant(item.second.commit["device"]);
|
||||||
case Role::ContactAction:
|
case Role::ContactAction:
|
||||||
return QVariant(item.second.commit["action"]);
|
return QVariant(item.second.commit["action"]);
|
||||||
case Role::PreviousBodies: {
|
case Role::PreviousBodies: {
|
||||||
|
|
|
@ -43,6 +43,8 @@ struct Info;
|
||||||
X(IsRead) \
|
X(IsRead) \
|
||||||
X(ContactAction) \
|
X(ContactAction) \
|
||||||
X(ActionUri) \
|
X(ActionUri) \
|
||||||
|
X(ConfId) \
|
||||||
|
X(DeviceId) \
|
||||||
X(LinkPreviewInfo) \
|
X(LinkPreviewInfo) \
|
||||||
X(Linkified) \
|
X(Linkified) \
|
||||||
X(PreviousBodies) \
|
X(PreviousBodies) \
|
||||||
|
@ -84,7 +86,9 @@ public:
|
||||||
interaction::Info message,
|
interaction::Info message,
|
||||||
bool beginning = false);
|
bool beginning = false);
|
||||||
iterator find(const QString& msgId);
|
iterator find(const QString& msgId);
|
||||||
|
iterator findActiveCall(const MapStringString& commit);
|
||||||
iterator erase(const iterator& it);
|
iterator erase(const iterator& it);
|
||||||
|
|
||||||
constIterator find(const QString& msgId) const;
|
constIterator find(const QString& msgId) const;
|
||||||
QPair<iterator, bool> insert(std::pair<QString, interaction::Info> message,
|
QPair<iterator, bool> insert(std::pair<QString, interaction::Info> message,
|
||||||
bool beginning = false);
|
bool beginning = false);
|
||||||
|
|
|
@ -92,8 +92,6 @@ public:
|
||||||
Q_EMIT this->volatileAccountDetailsChanged(QString(accountID.c_str()),
|
Q_EMIT this->volatileAccountDetailsChanged(QString(accountID.c_str()),
|
||||||
convertMap(details));
|
convertMap(details));
|
||||||
}),
|
}),
|
||||||
exportable_callback<ConfigurationSignal::Error>(
|
|
||||||
[this](int code) { Q_EMIT this->errorAlert(code); }),
|
|
||||||
exportable_callback<ConfigurationSignal::CertificateExpired>(
|
exportable_callback<ConfigurationSignal::CertificateExpired>(
|
||||||
[this](const std::string& certId) {
|
[this](const std::string& certId) {
|
||||||
Q_EMIT this->certificateExpired(QString(certId.c_str()));
|
Q_EMIT this->certificateExpired(QString(certId.c_str()));
|
||||||
|
@ -129,6 +127,11 @@ public:
|
||||||
QString(message_id.c_str()),
|
QString(message_id.c_str()),
|
||||||
state);
|
state);
|
||||||
}),
|
}),
|
||||||
|
exportable_callback<libjami::ConfigurationSignal::NeedsHost>(
|
||||||
|
[this](const std::string& account_id, const std::string& conversation_id) {
|
||||||
|
Q_EMIT this->needsHost(QString(account_id.c_str()),
|
||||||
|
QString(conversation_id.c_str()));
|
||||||
|
}),
|
||||||
exportable_callback<ConfigurationSignal::IncomingTrustRequest>(
|
exportable_callback<ConfigurationSignal::IncomingTrustRequest>(
|
||||||
[this](const std::string& accountId,
|
[this](const std::string& accountId,
|
||||||
const std::string& conversationId,
|
const std::string& conversationId,
|
||||||
|
@ -356,6 +359,14 @@ public:
|
||||||
QString(conversationId.c_str()),
|
QString(conversationId.c_str()),
|
||||||
code,
|
code,
|
||||||
QString(what.c_str()));
|
QString(what.c_str()));
|
||||||
|
}),
|
||||||
|
exportable_callback<ConfigurationSignal::ActiveCallsChanged>(
|
||||||
|
[this](const std::string& accountId,
|
||||||
|
const std::string& conversationId,
|
||||||
|
const std::vector<std::map<std::string, std::string>>& activeCalls) {
|
||||||
|
Q_EMIT activeCallsChanged(QString(accountId.c_str()),
|
||||||
|
QString(conversationId.c_str()),
|
||||||
|
convertVecMap(activeCalls));
|
||||||
})};
|
})};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +443,16 @@ public Q_SLOTS: // METHODS
|
||||||
|
|
||||||
QStringList getAccountList()
|
QStringList getAccountList()
|
||||||
{
|
{
|
||||||
QStringList temp = convertStringList(libjami::getAccountList());
|
return convertStringList(libjami::getAccountList());
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorMapStringString getActiveCalls(const QString& accountId, const QString& convId)
|
||||||
|
{
|
||||||
|
VectorMapStringString temp;
|
||||||
|
for (const auto& x :
|
||||||
|
libjami::getActiveCalls(accountId.toStdString(), convId.toStdString())) {
|
||||||
|
temp.push_back(convertMap(x));
|
||||||
|
}
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1195,7 +1215,6 @@ Q_SIGNALS: // SIGNALS
|
||||||
unsigned detail_code,
|
unsigned detail_code,
|
||||||
const QString& detail_str);
|
const QString& detail_str);
|
||||||
void stunStatusSuccess(const QString& message);
|
void stunStatusSuccess(const QString& message);
|
||||||
void errorAlert(int code);
|
|
||||||
void volatileAccountDetailsChanged(const QString& accountID, MapStringString details);
|
void volatileAccountDetailsChanged(const QString& accountID, MapStringString details);
|
||||||
void certificatePinned(const QString& certId);
|
void certificatePinned(const QString& certId);
|
||||||
void certificatePathPinned(const QString& path, const QStringList& certIds);
|
void certificatePathPinned(const QString& path, const QStringList& certIds);
|
||||||
|
@ -1222,6 +1241,7 @@ Q_SIGNALS: // SIGNALS
|
||||||
const QString& peer,
|
const QString& peer,
|
||||||
const QString& messageId,
|
const QString& messageId,
|
||||||
int status);
|
int status);
|
||||||
|
void needsHost(const QString& accountId, const QString& conversationId);
|
||||||
void nameRegistrationEnded(const QString& accountId, int status, const QString& name);
|
void nameRegistrationEnded(const QString& accountId, int status, const QString& name);
|
||||||
void registeredNameFound(const QString& accountId,
|
void registeredNameFound(const QString& accountId,
|
||||||
int status,
|
int status,
|
||||||
|
@ -1278,6 +1298,9 @@ Q_SIGNALS: // SIGNALS
|
||||||
const QString& conversationId,
|
const QString& conversationId,
|
||||||
int code,
|
int code,
|
||||||
const QString& what);
|
const QString& what);
|
||||||
|
void activeCallsChanged(const QString& accountId,
|
||||||
|
const QString& conversationId,
|
||||||
|
const VectorMapStringString& activeCalls);
|
||||||
void conversationPreferencesUpdated(const QString& accountId,
|
void conversationPreferencesUpdated(const QString& accountId,
|
||||||
const QString& conversationId,
|
const QString& conversationId,
|
||||||
const MapStringString& message);
|
const MapStringString& message);
|
||||||
|
|
Loading…
Add table
Reference in a new issue