1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-07-15 13:05:23 +02:00

TroubleshootingSetting: Connection monitoring

Change-Id: Idf922df701cdf2efc2d167362dca021897d8e2e5
This commit is contained in:
lcoursodon 2023-08-24 16:27:16 -04:00 committed by Sébastien Blin
parent e55eaa5d8e
commit 6bff3c54f1
18 changed files with 880 additions and 5 deletions

View file

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

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<path id="noun-connection-5025318" d="M18,6.3c1.2,0,2.2-1,2.2-2.1C20.1,3,19.2,2,18,2c-1.2,0-2.2,1-2.2,2.1c0,0.5,0.2,1.1,0.6,1.5
l-3.3,3.8c-0.8-0.6-2-0.5-2.7,0.2L7.3,7.1c0.5-0.8,0.2-1.9-0.6-2.4C5.9,4.2,4.8,4.5,4.3,5.3C3.8,6.1,4,7.2,4.9,7.7
C5.6,8.1,6.4,8,7,7.5l3.1,2.5c-0.5,0.8-0.4,1.9,0.3,2.6l-2.9,2.9c-1.1-0.7-2.5-0.4-3.2,0.7c-0.7,1.1-0.4,2.5,0.7,3.2
c1.1,0.7,2.5,0.4,3.2-0.7c0.6-0.9,0.5-2.1-0.3-2.9l2.9-2.9c0.3,0.2,0.7,0.3,1.2,0.3c0,0,0.1,0,0.1,0l0.8,4.8
c-1.1,0.3-1.7,1.4-1.4,2.5c0.3,1.1,1.4,1.7,2.5,1.4c1.1-0.3,1.7-1.4,1.4-2.5c-0.3-0.9-1-1.4-1.9-1.4c0,0-0.1,0-0.1,0l-0.8-4.8
c0.4-0.1,0.7-0.3,0.9-0.6l3.6,2.7c-0.4,0.8-0.2,1.8,0.6,2.2c0.8,0.4,1.8,0.2,2.2-0.6c0.4-0.8,0.2-1.8-0.6-2.2
c-0.6-0.4-1.4-0.3-1.9,0.2l-3.6-2.7c0.2-0.3,0.3-0.7,0.3-1.1c0-0.5-0.2-1.1-0.6-1.5l3.3-3.8C17.1,6.2,17.5,6.3,18,6.3L18,6.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<g id="noun-waiting-3611673" transform="translate(-13.64 -30.48)">
<path id="Path_278" d="M25.9,34.5c0.5,0,0.8-0.4,0.8-0.8c0-0.5-0.4-0.8-0.8-0.8c-0.5,0-0.8,0.4-0.8,0.8c0,0.2,0.1,0.4,0.2,0.6
C25.4,34.4,25.6,34.5,25.9,34.5z"/>
<path id="Path_279" d="M32.7,36.9c0.4-0.4,0.4-1.1,0-1.5c-0.4-0.4-1.1-0.4-1.5,0c-0.4,0.4-0.4,1.1,0,1.5c0.2,0.2,0.5,0.3,0.8,0.3
C32.2,37.2,32.5,37.1,32.7,36.9z"/>
<path id="Path_280" d="M34.5,41c-0.7,0-1.2,0.5-1.2,1.2s0.5,1.2,1.2,1.2c0.7,0,1.2-0.5,1.2-1.2c0-0.3-0.1-0.6-0.3-0.8
C35.1,41.1,34.8,41,34.5,41z"/>
<path id="Path_281" d="M31.9,47c-0.7,0-1.3,0.6-1.3,1.3c0,0.7,0.6,1.3,1.3,1.3c0.7,0,1.3-0.6,1.3-1.3c0-0.3-0.1-0.7-0.4-0.9
C32.6,47.1,32.3,47,31.9,47z"/>
<path id="Path_282" d="M25.9,49.4c-0.8,0-1.4,0.6-1.4,1.4c0,0.8,0.6,1.4,1.4,1.4c0.8,0,1.4-0.6,1.4-1.4c0-0.4-0.1-0.7-0.4-1
C26.6,49.5,26.2,49.4,25.9,49.4z"/>
<path id="Path_283" d="M18.7,47.2L18.7,47.2c-0.6,0.6-0.6,1.6,0,2.2c0.6,0.6,1.6,0.6,2.2,0c0.6-0.6,0.6-1.6,0-2.2
c-0.3-0.3-0.7-0.5-1.1-0.5C19.4,46.7,19,46.9,18.7,47.2z"/>
<path id="Path_284" d="M18.9,42.2c0-0.9-0.7-1.7-1.6-1.7s-1.7,0.7-1.7,1.6c0,0.9,0.7,1.7,1.6,1.7c0.4,0,0.9-0.2,1.2-0.5
C18.8,43,18.9,42.6,18.9,42.2z"/>
<path id="Path_285" d="M21.1,34.9c-0.7-0.3-1.5-0.1-1.9,0.6c-0.3,0.7-0.1,1.5,0.6,1.9c0.7,0.3,1.5,0.1,1.9-0.6
c0.1-0.2,0.2-0.5,0.1-0.7C21.8,35.5,21.5,35.1,21.1,34.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<int, QByteArray>
ConnectionInfoListModel::roleNames() const
{
using namespace ConnectionInfoList;
QHash<int, QByteArray> 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<typename T>
std::tuple<QVector<T>, QVector<T>>
getSetDiff(QVector<T> u, QVector<T> v)
{
using namespace std;
QVector<T> 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<QString> 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<QString> oldVector;
for (const auto& peerId : peerIds_) {
oldVector << peerId;
}
QVector<QString> newVector;
for (const auto& peerId : newPeerIds) {
newVector << peerId;
}
std::sort(oldVector.begin(), oldVector.end());
std::sort(newVector.begin(), newVector.end());
QVector<QString> 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();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<int, QByteArray> roleNames() const override;
Q_INVOKABLE void update();
private:
using Role = ConnectionInfoList::Role;
VectorMapStringString connectionInfoList_;
QVector<QString> peerIds_;
QMap<QString, QMap<QString, QMap<QString, QVariant>>> peerData_;
void aggregateData();
void resetData();
};

View file

@ -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: ")
}

View file

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

View file

@ -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()
{

View file

@ -21,6 +21,7 @@
#include "qmladapterbase.h"
#include "smartlistmodel.h"
#include "conversationlistmodel.h"
#include "connectioninfolistmodel.h"
#include <QObject>
#include <QSortFilterProxyModel>
@ -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> smartListModel_;
QScopedPointer<SelectableProxyModel> selectableProxyModel_;
QScopedPointer<ConnectionInfoListModel> connectionInfoListModel_;
QStringList defaultModerators_;

View file

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

View file

@ -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();

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
}
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
});
}
}
}
}
}
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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());