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:
parent
e55eaa5d8e
commit
6bff3c54f1
18 changed files with 880 additions and 5 deletions
|
@ -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")
|
||||
|
|
12
resources/icons/Connected_Black_24dp.svg
Normal file
12
resources/icons/Connected_Black_24dp.svg
Normal 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 |
23
resources/icons/Connecting_Black_24dp.svg
Normal file
23
resources/icons/Connecting_Black_24dp.svg
Normal 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 |
228
src/app/connectioninfolistmodel.cpp
Normal file
228
src/app/connectioninfolistmodel.cpp
Normal 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();
|
||||
}
|
64
src/app/connectioninfolistmodel.h
Normal file
64
src/app/connectioninfolistmodel.h
Normal 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();
|
||||
};
|
|
@ -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: ")
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
|
|
60
src/app/settingsview/components/ChannelsPopup.qml
Normal file
60
src/app/settingsview/components/ChannelsPopup.qml
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
381
src/app/settingsview/components/ConnectionMonitoringTable.qml
Normal file
381
src/app/settingsview/components/ConnectionMonitoringTable.qml
Normal 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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Add table
Reference in a new issue