diff --git a/CMakeLists.txt b/CMakeLists.txt
index a91d2ed1..c7c66754 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -244,7 +244,8 @@ set(COMMON_SOURCES
${APP_SRC_DIR}/messageparser.cpp
${APP_SRC_DIR}/previewengine.cpp
${APP_SRC_DIR}/imagedownloader.cpp
- ${APP_SRC_DIR}/pluginversionmanager.cpp)
+ ${APP_SRC_DIR}/pluginversionmanager.cpp
+ ${APP_SRC_DIR}/connectioninfolistmodel.cpp)
set(COMMON_HEADERS
${APP_SRC_DIR}/avatarimageprovider.h
@@ -310,8 +311,8 @@ set(COMMON_HEADERS
${APP_SRC_DIR}/messageparser.h
${APP_SRC_DIR}/htmlparser.h
${APP_SRC_DIR}/imagedownloader.h
- ${APP_SRC_DIR}/pluginversionmanager.h)
-
+ ${APP_SRC_DIR}/pluginversionmanager.h
+ ${APP_SRC_DIR}/connectioninfolistmodel.h)
# For libavutil/avframe.
set(LIBJAMI_CONTRIB_DIR "${DAEMON_DIR}/contrib")
diff --git a/resources/icons/Connected_Black_24dp.svg b/resources/icons/Connected_Black_24dp.svg
new file mode 100644
index 00000000..70325ffa
--- /dev/null
+++ b/resources/icons/Connected_Black_24dp.svg
@@ -0,0 +1,12 @@
+
+
+
diff --git a/resources/icons/Connecting_Black_24dp.svg b/resources/icons/Connecting_Black_24dp.svg
new file mode 100644
index 00000000..b35b6e7b
--- /dev/null
+++ b/resources/icons/Connecting_Black_24dp.svg
@@ -0,0 +1,23 @@
+
+
+
diff --git a/src/app/connectioninfolistmodel.cpp b/src/app/connectioninfolistmodel.cpp
new file mode 100644
index 00000000..99f1a808
--- /dev/null
+++ b/src/app/connectioninfolistmodel.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2023 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 .
+ */
+
+#include "connectioninfolistmodel.h"
+
+ConnectionInfoListModel::ConnectionInfoListModel(LRCInstance* instance, QObject* parent)
+ : AbstractListModelBase(parent)
+{
+ lrcInstance_ = instance;
+ connect(lrcInstance_,
+ &LRCInstance::currentAccountIdChanged,
+ this,
+ &ConnectionInfoListModel::resetData);
+}
+
+int
+ConnectionInfoListModel::rowCount(const QModelIndex& parent) const
+{
+ return peerIds_.size();
+}
+
+QVariant
+ConnectionInfoListModel::data(const QModelIndex& index, int role) const
+{
+ const auto accountId = lrcInstance_->get_currentAccountId();
+
+ if (accountId.isEmpty()) {
+ qWarning() << "ConnectionInfoListModel::data: accountId or peerID is empty";
+ return {};
+ }
+ const auto peerId = peerIds_[index.row()];
+ const auto peerData = peerData_[peerId];
+
+ switch (role) {
+ case ConnectionInfoList::ChannelsMap: {
+ QVariantMap channelsMapMap;
+ int i = 0;
+ for (const auto& device : peerData.keys()) {
+ QString channelsId = peerData[device]["id"].toString();
+ QVariantMap channelsMap;
+ const auto channelInfoList = lrcInstance_->getChannelList(accountId, channelsId);
+ for (const auto& channelInfo : channelInfoList) {
+ channelsMap.insert(channelInfo["id"], channelInfo["name"]);
+ }
+ channelsMapMap.insert(QString::number(i++), channelsMap);
+ }
+ return QVariant(channelsMapMap);
+ }
+ case ConnectionInfoList::ConnectionDatas: {
+ QString peerString;
+ peerString += "Peer:" + peerId;
+ for (const auto& device : peerData.keys()) {
+ peerString += "{";
+ peerString += "Device:" + device + ",";
+ peerString += "Status:" + peerData[device]["status"].toString() + ",";
+ peerString += "Channels:" + peerData[device]["channels"].toString() + ",";
+ peerString += "Remote Address" + peerData[device]["remoteAddress"].toString();
+ peerString += "}";
+ }
+ return peerString;
+ }
+ case ConnectionInfoList::PeerId:
+ return peerId;
+ case ConnectionInfoList::RemoteAddress: {
+ QVariantMap remoteAddressMap;
+ int i = 0;
+ for (const auto& device : peerData.keys()) {
+ remoteAddressMap.insert(QString::number(i++), peerData[device]["remoteAddress"]);
+ }
+ return QVariant(remoteAddressMap);
+ }
+ case ConnectionInfoList::DeviceId: {
+ QVariantMap deviceMap;
+ int i = 0;
+ for (const auto& device : peerData.keys()) {
+ deviceMap.insert(QString::number(i++), device);
+ }
+ return QVariant(deviceMap);
+ }
+ case ConnectionInfoList::Status: {
+ QVariantMap statusMap;
+ int i = 0;
+ for (const auto& device : peerData.keys()) {
+ statusMap.insert(QString::number(i++), peerData[device]["status"]);
+ }
+ return QVariantMap(statusMap);
+ }
+ case ConnectionInfoList::Channels: {
+ QVariantMap channelsMap;
+ int i = 0;
+ for (const auto& device : peerData.keys()) {
+ channelsMap.insert(QString::number(i++), peerData[device]["channels"]);
+ }
+ return QVariant(channelsMap);
+ }
+ case ConnectionInfoList::Count:
+ return peerData.size();
+ }
+ return {};
+}
+
+QHash
+ConnectionInfoListModel::roleNames() const
+{
+ using namespace ConnectionInfoList;
+ QHash roles;
+#define X(role) roles[role] = #role;
+ CONNECTONINFO_ROLES
+#undef X
+ return roles;
+}
+
+void
+ConnectionInfoListModel::update()
+{
+ const auto accountId = lrcInstance_->get_currentAccountId();
+ if (accountId.isEmpty()) {
+ return;
+ }
+ aggregateData();
+}
+
+template
+std::tuple, QVector>
+getSetDiff(QVector u, QVector v)
+{
+ using namespace std;
+ QVector a, r;
+ set_difference(v.begin(), v.end(), u.begin(), u.end(), inserter(a, a.begin()));
+ set_difference(u.begin(), u.end(), v.begin(), v.end(), inserter(r, r.begin()));
+ return {a, r};
+}
+
+void
+ConnectionInfoListModel::aggregateData()
+{
+ const auto accountId = lrcInstance_->get_currentAccountId();
+ if (accountId.isEmpty()) {
+ return;
+ }
+
+ connectionInfoList_ = lrcInstance_->getConnectionList(accountId);
+
+ peerData_ = {};
+
+ QSet newPeerIds;
+
+ for (const auto& connectionInfo : connectionInfoList_) {
+ if (!connectionInfo["peer"].isEmpty()) {
+ newPeerIds.insert(connectionInfo["peer"]);
+ }
+ const auto channelInfoList = lrcInstance_->getChannelList(accountId, connectionInfo["id"]);
+ peerData_[connectionInfo["peer"]][connectionInfo["device"]] = {};
+ peerData_[connectionInfo["peer"]][connectionInfo["device"]]["status"]
+ = connectionInfo["status"];
+ peerData_[connectionInfo["peer"]][connectionInfo["device"]]["channels"] = channelInfoList
+ .size();
+ peerData_[connectionInfo["peer"]][connectionInfo["device"]]["id"] = connectionInfo["id"];
+ peerData_[connectionInfo["peer"]][connectionInfo["device"]]["remoteAddress"]
+ = connectionInfo["remoteAddress"];
+ }
+
+ QVector oldVector;
+ for (const auto& peerId : peerIds_) {
+ oldVector << peerId;
+ }
+ QVector newVector;
+ for (const auto& peerId : newPeerIds) {
+ newVector << peerId;
+ }
+
+ std::sort(oldVector.begin(), oldVector.end());
+ std::sort(newVector.begin(), newVector.end());
+
+ QVector removed, added;
+ std::tie(added, removed) = getSetDiff(oldVector, newVector);
+ Q_FOREACH (const auto& key, added) {
+ auto index = std::distance(newVector.begin(),
+ std::find(newVector.begin(), newVector.end(), key));
+ beginInsertRows(QModelIndex(), index, index);
+ peerIds_.insert(index, key);
+ endInsertRows();
+ }
+ Q_FOREACH (const auto& key, removed) {
+ auto index = std::distance(oldVector.begin(),
+ std::find(oldVector.begin(), oldVector.end(), key));
+ beginRemoveRows(QModelIndex(), index, index);
+ if (peerIds_.size() > index) {
+ peerIds_.remove(index);
+ } else {
+ qWarning() << "ConnectionInfoListModel::aggregateData: index out of range";
+ qWarning() << "index: " << index;
+ qWarning() << "key: " << key;
+ }
+ endRemoveRows();
+ }
+
+ // HACK: loop through all the peerIds_ and update the data for each one.
+ // This is not efficient, but it works.
+ Q_FOREACH (const auto& peerId, peerIds_) {
+ auto index = std::distance(peerIds_.begin(),
+ std::find(peerIds_.begin(), peerIds_.end(), peerId));
+ Q_EMIT dataChanged(this->index(index), this->index(index));
+ }
+}
+
+void
+ConnectionInfoListModel::resetData()
+{
+ beginResetModel();
+ peerIds_.clear();
+ peerData_.clear();
+ endResetModel();
+}
\ No newline at end of file
diff --git a/src/app/connectioninfolistmodel.h b/src/app/connectioninfolistmodel.h
new file mode 100644
index 00000000..74daa7a2
--- /dev/null
+++ b/src/app/connectioninfolistmodel.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 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 .
+ */
+
+#pragma once
+
+#include "abstractlistmodelbase.h"
+
+#define CONNECTONINFO_ROLES \
+ X(ConnectionDatas) \
+ X(ChannelsMap) \
+ X(PeerName) \
+ X(PeerId) \
+ X(DeviceId) \
+ X(Status) \
+ X(Channels) \
+ X(RemoteAddress) \
+ X(Count) // this is the number of connections (convenience)
+
+namespace ConnectionInfoList {
+Q_NAMESPACE
+enum Role {
+ DummyRole = Qt::UserRole + 1,
+#define X(role) role,
+ CONNECTONINFO_ROLES
+#undef X
+};
+Q_ENUM_NS(Role)
+} // namespace ConnectionInfoList
+
+class ConnectionInfoListModel : public AbstractListModelBase
+{
+public:
+ explicit ConnectionInfoListModel(LRCInstance* instance, QObject* parent = nullptr);
+
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+ QHash roleNames() const override;
+
+ Q_INVOKABLE void update();
+
+private:
+ using Role = ConnectionInfoList::Role;
+
+ VectorMapStringString connectionInfoList_;
+
+ QVector peerIds_;
+ QMap>> peerData_;
+ void aggregateData();
+ void resetData();
+};
diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml
index 6bec14fb..f3c9d145 100644
--- a/src/app/constant/JamiStrings.qml
+++ b/src/app/constant/JamiStrings.qml
@@ -850,4 +850,16 @@ Item {
property string shiftEnter: qsTr("SHIFT+ENTER")
property string textFormattingDescription: qsTr("ENTER or SHIFT+ENTER to insert a new line")
property string textFormatting: qsTr("Text formatting")
+
+ //Connection monitoring
+ property string connected: qsTr("Connected")
+ property string connectingTLS: qsTr("Connecting TLS")
+ property string connectingICE: qsTr("Connecting ICE")
+ property string connecting: qsTr("Connecting")
+ property string waiting: qsTr("Waiting")
+ property string contact: qsTr("Contact")
+ property string connection: qsTr("Connection")
+ property string channels: qsTr("Channels")
+ property string copyAllData: qsTr("Copy all data")
+ property string remote: qsTr("Remote: ")
}
diff --git a/src/app/constant/JamiTheme.qml b/src/app/constant/JamiTheme.qml
index e29815fd..ed15e162 100644
--- a/src/app/constant/JamiTheme.qml
+++ b/src/app/constant/JamiTheme.qml
@@ -664,6 +664,11 @@ Item {
property color donationBackgroundColor: "#D5E4EF"
property string donationUrl: "https://jami.net/donate/"
+ //Connection monitoring
+ property color connectionMonitoringTableColor1: "#f0efef"
+ property color connectionMonitoringTableColor2: "#f6f5f5"
+ property color connectionMonitoringHeaderColor: "#d1d1d1"
+
function setTheme(dark) {
darkTheme = dark;
}
diff --git a/src/app/contactadapter.cpp b/src/app/contactadapter.cpp
index fb49f41d..7585f346 100644
--- a/src/app/contactadapter.cpp
+++ b/src/app/contactadapter.cpp
@@ -21,10 +21,16 @@
#include "contactadapter.h"
#include "lrcinstance.h"
+#include "qmlregister.h"
ContactAdapter::ContactAdapter(LRCInstance* instance, QObject* parent)
: QmlAdapterBase(instance, parent)
+ , connectionInfoListModel_(new ConnectionInfoListModel(lrcInstance_, this))
{
+ QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS,
+ connectionInfoListModel_.get(),
+ "ConnectionInfoListModel");
+
selectableProxyModel_.reset(new SelectableProxyModel(this));
if (lrcInstance_) {
connectSignals();
@@ -246,6 +252,12 @@ ContactAdapter::removeContact(const QString& peerUri, bool banContact)
accInfo.contactModel->removeContact(peerUri, banContact);
}
+void
+ContactAdapter::updateConnectionInfo()
+{
+ connectionInfoListModel_->update();
+}
+
void
ContactAdapter::connectSignals()
{
diff --git a/src/app/contactadapter.h b/src/app/contactadapter.h
index c3666eec..ca92a669 100644
--- a/src/app/contactadapter.h
+++ b/src/app/contactadapter.h
@@ -21,6 +21,7 @@
#include "qmladapterbase.h"
#include "smartlistmodel.h"
#include "conversationlistmodel.h"
+#include "connectioninfolistmodel.h"
#include
#include
@@ -90,6 +91,7 @@ public:
Q_INVOKABLE void setSearchFilter(const QString& filter);
Q_INVOKABLE void contactSelected(int index);
Q_INVOKABLE void removeContact(const QString& peerUri, bool banContact);
+ Q_INVOKABLE void updateConnectionInfo();
void connectSignals();
@@ -104,6 +106,7 @@ private:
SmartListModel::Type listModeltype_;
QScopedPointer smartListModel_;
QScopedPointer selectableProxyModel_;
+ QScopedPointer connectionInfoListModel_;
QStringList defaultModerators_;
diff --git a/src/app/lrcinstance.cpp b/src/app/lrcinstance.cpp
index 02bafe4f..0cd74c32 100644
--- a/src/app/lrcinstance.cpp
+++ b/src/app/lrcinstance.cpp
@@ -458,3 +458,15 @@ LRCInstance::set_selectedConvUid(QString selectedConvUid)
Q_EMIT selectedConvUidChanged();
}
}
+
+VectorMapStringString
+LRCInstance::getConnectionList(const QString& accountId, const QString& uid)
+{
+ return Lrc::getConnectionList(accountId, uid);
+}
+
+VectorMapStringString
+LRCInstance::getChannelList(const QString& accountId, const QString& uid)
+{
+ return Lrc::getChannelList(accountId, uid);
+}
\ No newline at end of file
diff --git a/src/app/lrcinstance.h b/src/app/lrcinstance.h
index 08075930..502165c3 100644
--- a/src/app/lrcinstance.h
+++ b/src/app/lrcinstance.h
@@ -135,6 +135,10 @@ public:
return debugMode_;
}
+ VectorMapStringString getConnectionList(const QString& accountId, const QString& uid = {});
+
+ VectorMapStringString getChannelList(const QString& accountId, const QString& uid = {});
+
Q_SIGNALS:
void accountListChanged();
void selectedConvUidChanged();
diff --git a/src/app/settingsview/components/ChannelsPopup.qml b/src/app/settingsview/components/ChannelsPopup.qml
new file mode 100644
index 00000000..4ef7ffa3
--- /dev/null
+++ b/src/app/settingsview/components/ChannelsPopup.qml
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 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 .
+ */
+import QtQuick
+import QtQuick.Controls
+import Qt5Compat.GraphicalEffects
+import QtQuick.Layouts
+import net.jami.Models 1.1
+import net.jami.Adapters 1.1
+import net.jami.Constants 1.1
+
+Popup {
+ id: popup
+ width: textComponent.contentWidth + 40 < popup.maxWidth - 20 ? textComponent.contentWidth + 40 : popup.maxWidth - 20
+ height: textComponent.contentHeight + 40 < 350 ? textComponent.contentHeight + 40 : 350
+ property string text: ""
+ property int maxWidth: 0
+ x: -1 * (popup.width - 20)
+
+ Rectangle {
+ anchors.fill: parent
+ color: JamiTheme.transparentColor
+
+ Flickable {
+ anchors.fill: parent
+ contentHeight: textComponent.contentHeight + 10
+ contentWidth: textComponent.contentWidth + 20
+ clip: true
+ ScrollBar.vertical: ScrollBar {
+ active: contentHeight > height
+ }
+ ScrollBar.horizontal: ScrollBar {
+ active: contentWidth > width
+ }
+ contentX: 10
+ contentY: 10
+
+ Text {
+ id: textComponent
+ width: popup.maxWidth - 20
+ elide: Text.ElideRight
+ horizontalAlignment: Text.AlignLeft
+ text: popup.text
+ }
+ }
+ }
+}
diff --git a/src/app/settingsview/components/ConnectionMonitoringTable.qml b/src/app/settingsview/components/ConnectionMonitoringTable.qml
new file mode 100644
index 00000000..c770495f
--- /dev/null
+++ b/src/app/settingsview/components/ConnectionMonitoringTable.qml
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2023 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 .
+ */
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+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 "../js/logviewwindowcreation.js" as LogViewWindowCreation
+
+ListView {
+ id: listview
+ height: contentItem.childrenRect.height
+ anchors.top: parent.top
+ anchors.topMargin: 10
+
+ spacing: 5
+ cacheBuffer: 10
+
+ property int rota: 0
+
+ header: Rectangle {
+ height: 55
+ width: connectionMonitoringTable.width
+ Rectangle {
+ color: JamiTheme.connectionMonitoringHeaderColor
+ anchors.top: parent.top
+ height: 50
+ width: connectionMonitoringTable.width
+
+ RowLayout {
+ anchors.fill: parent
+ Rectangle {
+ id: profile
+ height: 50
+ Layout.leftMargin: 5
+ Layout.preferredWidth: 210
+ color: JamiTheme.transparentColor
+ Text {
+ id: textImage
+ anchors.leftMargin: 10
+ anchors.verticalCenter: parent.verticalCenter
+ text: JamiStrings.contact
+ }
+ }
+
+ Rectangle {
+ id: device
+ Layout.fillWidth: true
+ height: 50
+ color: JamiTheme.transparentColor
+ Text {
+ id: deviceText
+ anchors.verticalCenter: parent.verticalCenter
+ text: JamiStrings.device
+ }
+ }
+
+ Rectangle {
+ id: connection
+ width: 130
+ height: 50
+ radius: 5
+ color: JamiTheme.transparentColor
+ Text {
+ id: connectionText
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 10
+ text: JamiStrings.connection
+ }
+ }
+
+ Rectangle {
+ id: channel
+ height: 50
+ width: 70
+ color: JamiTheme.transparentColor
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ text: JamiStrings.channels
+ }
+ }
+ }
+ }
+ }
+
+ model: ConnectionInfoListModel
+ Timer {
+ interval: 1000
+ running: root.visible
+ repeat: true
+ onTriggered: {
+ ContactAdapter.updateConnectionInfo();
+ listview.rota = listview.rota + 5;
+ }
+ }
+
+ delegate: Rectangle {
+ id: delegate
+ height: Count == 0 ? 0 : 10 + 40 * Count
+ width: connectionMonitoringTable.width
+ color: index % 2 === 0 ? JamiTheme.connectionMonitoringTableColor1 : JamiTheme.connectionMonitoringTableColor2
+
+ ListView {
+ id: listView2
+ height: 40 * Count
+
+ anchors.top: delegate.top
+
+ spacing: 0
+
+ model: Count
+
+ delegate: RowLayout {
+ id: rowLayoutDelegate
+ height: 40
+ width: connectionMonitoringTable.width
+
+ Rectangle {
+ id: profile
+ height: 50
+ Layout.leftMargin: 5
+ Layout.preferredWidth: 210
+ color: JamiTheme.transparentColor
+ Avatar {
+ id: avatar
+ visible: index == 0
+ anchors.left: parent.left
+ height: 40
+ width: 40
+ anchors.verticalCenter: parent.verticalCenter
+ imageId: PeerId
+ mode: Avatar.Mode.Contact
+ }
+ Rectangle {
+ id: usernameRect
+ anchors.left: avatar.right
+ anchors.verticalCenter: parent.verticalCenter
+ width: profile.width - 50
+ height: 40
+ color: JamiTheme.transparentColor
+
+ Rectangle {
+ id: usernameRect2
+ visible: index == 0
+ width: profile.width - 50
+ height: 20
+ anchors.leftMargin: 10
+ anchors.top: parent.top
+ anchors.left: parent.left
+ color: JamiTheme.transparentColor
+
+ Text {
+ id: usernameText
+ text: UtilsAdapter.getBestNameForUri(CurrentAccount.id, PeerId)
+ elide: Text.ElideRight
+ }
+ }
+
+ Rectangle {
+ width: profile.width - 50
+ height: 20
+ anchors.leftMargin: 10
+ anchors.top: usernameRect2.bottom
+ anchors.left: parent.left
+ visible: usernameRect2.visible && (UtilsAdapter.getBestIdForUri(CurrentAccount.id, PeerId) != UtilsAdapter.getBestNameForUri(CurrentAccount.id, PeerId))
+ color: JamiTheme.transparentColor
+
+ Text {
+ id: idText
+ anchors.fill: parent
+ text: UtilsAdapter.getBestIdForUri(CurrentAccount.id, PeerId)
+ font.pixelSize: 12
+ font.underline: usernameText.font.underline
+ elide: Text.ElideRight
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: {
+ usernameText.font.underline = true;
+ tooltipContact.text = JamiStrings.copyAllData;
+ }
+ onExited: {
+ usernameText.font.underline = false;
+ tooltipContact.text = JamiStrings.copyAllData;
+ }
+
+ ToolTip {
+ id: tooltipContact
+ visible: usernameText.font.underline
+ text: JamiStrings.copyAllData
+ }
+ onClicked: {
+ tooltipContact.text = JamiStrings.logsViewCopied;
+ UtilsAdapter.setClipboardText(ConnectionDatas);
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ height: 40
+ Layout.fillWidth: true
+ color: delegate.color
+ Text {
+ id: delegateDeviceText
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ text: {
+ if (DeviceId[index] != undefined) {
+ return DeviceId[index];
+ } else {
+ return "";
+ }
+ }
+ elide: Text.ElideMiddle
+ width: parent.width - 10
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: {
+ delegateDeviceText.font.underline = true;
+ }
+ onExited: {
+ delegateDeviceText.font.underline = false;
+ tooltipDevice.text = delegateDeviceText.text;
+ }
+
+ ToolTip {
+ id: tooltipDevice
+ visible: delegateDeviceText.font.underline
+ text: delegateDeviceText.text
+ }
+ onClicked: {
+ tooltipDevice.text = delegateDeviceText.text + " (" + JamiStrings.logsViewCopied + ")";
+ UtilsAdapter.setClipboardText(delegateDeviceText.text);
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ id: connectionRectangle
+ color: delegate.color
+ height: 40
+ Layout.preferredWidth: 130
+ property var status: Status[index]
+ ResponsiveImage {
+ id: connectionImage
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ rotation: connectionRectangle.status == 0 ? 0 : listview.rota
+ source: {
+ if (connectionRectangle.status == 0) {
+ return JamiResources.connected_black_24dp_svg;
+ } else {
+ return JamiResources.connecting_black_24dp_svg;
+ }
+ }
+ color: {
+ if (connectionRectangle.status == 0) {
+ return "#009c7f";
+ } else {
+ if (connectionRectangle.status == 4) {
+ return "red";
+ } else {
+ return "#ff8100";
+ }
+ }
+ }
+ }
+ Text {
+ id: connectionText
+ anchors.left: connectionImage.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 5
+ text: if (connectionRectangle.status == 0) {
+ return JamiStrings.connected;
+ } else {
+ if (connectionRectangle.status == 1) {
+ return JamiStrings.connectingTLS;
+ } else {
+ if (connectionRectangle.status == 2) {
+ return JamiStrings.connectingICE;
+ } else {
+ if (connectionRectangle.status == 3) {
+ return JamiStrings.connecting;
+ } else {
+ return JamiStrings.waiting;
+ }
+ }
+ }
+ }
+ color: connectionImage.color
+ property var tooltipText: JamiStrings.remote + RemoteAddress[index]
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: {
+ connectionText.font.underline = true;
+ }
+ onExited: {
+ connectionText.font.underline = false;
+ }
+
+ ToolTip {
+ visible: connectionText.font.underline
+ text: connectionText.tooltipText
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ id: channelDelegateRectangle
+ height: 40
+ Layout.preferredWidth: 70
+ color: delegate.color
+ Text {
+ id: channelText
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 10
+ anchors.left: parent.left
+ text: {
+ if (Channels[index] != undefined) {
+ return Channels[index];
+ } else {
+ return "";
+ }
+ }
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+
+ onExited: {
+ channelText.font.underline = false;
+ }
+
+ onEntered: {
+ channelText.font.underline = true;
+ }
+
+ onClicked: {
+ var output = "";
+ var channelMap = ChannelsMap[index];
+ for (var key in channelMap) {
+ var value = channelMap[key];
+ var keyHexa = parseInt(key, 16).toString();
+ output += keyHexa + " : " + value + "\n";
+ }
+ viewCoordinator.presentDialog(parent, "settingsview/components/ChannelsPopup.qml", {
+ "text": output,
+ "maxWidth": connectionMonitoringTable.width
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/app/settingsview/components/SettingsPageBase.qml b/src/app/settingsview/components/SettingsPageBase.qml
index 69cab4ac..cc982f38 100644
--- a/src/app/settingsview/components/SettingsPageBase.qml
+++ b/src/app/settingsview/components/SettingsPageBase.qml
@@ -27,6 +27,7 @@ JamiSplitView {
id: root
required property Item flickableContent
property real contentFlickableWidth: Math.min(JamiTheme.maximumWidthSettingsView, settingsPage.width - 2 * JamiTheme.preferredSettingsMarginSize)
+ property real tableWidth: Math.min(JamiTheme.maximumWidthSettingsView * 2, settingsPage.width - 2 * JamiTheme.preferredSettingsMarginSize)
property alias title: settingsPage.title
property color backgroundColor: JamiTheme.secondaryBackgroundColor
property alias pageContainer: settingsPage
diff --git a/src/app/settingsview/components/TroubleshootSettingsPage.qml b/src/app/settingsview/components/TroubleshootSettingsPage.qml
index 63f5aded..6e1be6d4 100644
--- a/src/app/settingsview/components/TroubleshootSettingsPage.qml
+++ b/src/app/settingsview/components/TroubleshootSettingsPage.qml
@@ -29,11 +29,18 @@ import "../js/logviewwindowcreation.js" as LogViewWindowCreation
SettingsPageBase {
id: root
+ Layout.fillWidth: true
+
+ readonly property string baseProviderPrefix: 'image://avatarImage'
+
+ property string typePrefix: 'contact'
+ property string divider: '_'
+
property int itemWidth
title: JamiStrings.troubleshootTitle
- flickableContent: ColumnLayout {
+ flickableContent: Column {
id: troubleshootSettingsColumnLayout
width: contentFlickableWidth
@@ -42,7 +49,7 @@ SettingsPageBase {
anchors.leftMargin: JamiTheme.preferredSettingsMarginSize
RowLayout {
-
+ id: rawLayout
Text {
Layout.fillWidth: true
Layout.preferredHeight: 30
@@ -85,5 +92,15 @@ SettingsPageBase {
}
}
}
+
+ Rectangle {
+ id: connectionMonitoringTable
+ height: listview.childrenRect.height + 60
+ width: tableWidth
+
+ ConnectionMonitoringTable {
+ id: listview
+ }
+ }
}
}
diff --git a/src/libclient/api/lrc.h b/src/libclient/api/lrc.h
index 1151c0be..03c61727 100644
--- a/src/libclient/api/lrc.h
+++ b/src/libclient/api/lrc.h
@@ -109,6 +109,16 @@ public:
*/
static VectorString getConferences(const QString& accountId = "");
+ /**
+ * Get connection list from daemon
+ */
+ static VectorMapStringString getConnectionList(const QString& accountId, const QString& uid);
+
+ /**
+ * Get channel list from daemon
+ */
+ static VectorMapStringString getChannelList(const QString& accountId, const QString& uid);
+
/**
* Preference
*/
diff --git a/src/libclient/lrc.cpp b/src/libclient/lrc.cpp
index c2fb60af..330b9475 100644
--- a/src/libclient/lrc.cpp
+++ b/src/libclient/lrc.cpp
@@ -211,6 +211,18 @@ Lrc::getConferences(const QString& accountId)
return result;
}
+VectorMapStringString
+Lrc::getConnectionList(const QString& accountId, const QString& uid)
+{
+ return ConfigurationManager::instance().getConnectionList(accountId, uid);
+}
+
+VectorMapStringString
+Lrc::getChannelList(const QString& accountId, const QString& uid)
+{
+ return ConfigurationManager::instance().getChannelList(accountId, uid);
+}
+
bool
isFinished(const QString& callState)
{
diff --git a/src/libclient/qtwrapper/configurationmanager_wrap.h b/src/libclient/qtwrapper/configurationmanager_wrap.h
index 2eb19ef9..2ecafd2a 100644
--- a/src/libclient/qtwrapper/configurationmanager_wrap.h
+++ b/src/libclient/qtwrapper/configurationmanager_wrap.h
@@ -1016,6 +1016,24 @@ public Q_SLOTS: // METHODS
libjami::setAllModerators(accountID.toStdString(), allModerators);
}
+ VectorMapStringString getConnectionList(const QString& accountId, const QString& uid)
+ {
+ VectorMapStringString temp;
+ for (auto x : libjami::getConnectionList(accountId.toStdString(), uid.toStdString())) {
+ temp.push_back(convertMap(x));
+ }
+ return temp;
+ }
+
+ VectorMapStringString getChannelList(const QString& accountId, const QString& uid)
+ {
+ VectorMapStringString temp;
+ for (auto x : libjami::getChannelList(accountId.toStdString(), uid.toStdString())) {
+ temp.push_back(convertMap(x));
+ }
+ return temp;
+ }
+
bool isAllModerators(const QString& accountID)
{
return libjami::isAllModerators(accountID.toStdString());