1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-08-10 09:45:40 +02:00

chatview: display signaled back-end errors

A signal exists for showing errors to the user to make failing cases
more explicit. With this patch, errors detected are displayed to
the end user so that they know that an error occured and what
kind of error occured.

Change-Id: Ib2d4d4fdb171235e0598de0f1c190b8fd0fcc336
This commit is contained in:
Sébastien Blin 2022-08-12 16:47:20 -04:00
parent b0f3bc5572
commit ce3afea995
14 changed files with 250 additions and 0 deletions

View file

@ -98,6 +98,7 @@
<file>src/app/mainview/components/SidePanel.qml</file>
<file>src/app/mainview/components/WelcomePage.qml</file>
<file>src/app/mainview/components/ChatView.qml</file>
<file>src/app/mainview/components/ConversationErrorsRow.qml</file>
<file>src/app/mainview/components/NewSwarmPage.qml</file>
<file>src/app/mainview/components/ChatViewHeader.qml</file>
<file>src/app/mainview/components/AccountComboBox.qml</file>

View file

@ -301,6 +301,7 @@ Item {
property string placeVideoCall: qsTr("Place video call")
property string showPlugins: qsTr("Show available plugins")
property string addToConversations: qsTr("Add to conversations")
property string backendError: qsTr("This is the error from the backend: %0")
// Chatview footer
property string jumpToLatest: qsTr("Jump to latest")

View file

@ -495,6 +495,13 @@ ConversationsAdapter::updateConversationTitle(const QString& convId, const QStri
convModel->updateConversationInfos(convId, details);
}
void
ConversationsAdapter::popFrontError(const QString& convId)
{
auto convModel = lrcInstance_->getCurrentConversationModel();
convModel->popFrontError(convId);
}
void
ConversationsAdapter::updateConversationDescription(const QString& convId,
const QString& newDescription)

View file

@ -57,6 +57,7 @@ public:
Q_INVOKABLE QVariantMap getConvInfoMap(const QString& convId);
Q_INVOKABLE void restartConversation(const QString& convId);
Q_INVOKABLE void updateConversationTitle(const QString& convId, const QString& newTitle);
Q_INVOKABLE void popFrontError(const QString& convId);
Q_INVOKABLE void updateConversationDescription(const QString& convId,
const QString& newDescription);

View file

@ -104,6 +104,7 @@ CurrentConversation::updateData()
} catch (...) {
qWarning() << "Can't update current conversation data for" << convId;
}
updateErrors(convId);
}
void
@ -142,6 +143,11 @@ CurrentConversation::connectModel()
this,
&CurrentConversation::onProfileUpdated,
Qt::UniqueConnection);
connect(lrcInstance_->getCurrentConversationModel(),
&ConversationModel::onConversationErrorsUpdated,
this,
&CurrentConversation::updateErrors,
Qt::UniqueConnection);
}
void
@ -149,3 +155,36 @@ CurrentConversation::showSwarmDetails() const
{
Q_EMIT showDetails();
}
void
CurrentConversation::updateErrors(const QString& convId)
{
if (convId != id_)
return;
try {
const auto& convModel = lrcInstance_->getCurrentConversationModel();
if (auto optConv = convModel->getConversationForUid(convId)) {
auto& convInfo = optConv->get();
QStringList newErrors;
QStringList newBackendErr;
for (const auto& [code, error]: convInfo.errors) {
if (code == 1) {
newErrors.append(tr("An error occurred while fetching this repository"));
} else if (code == 2) {
newErrors.append(tr("The conversation's mode is un-recognized"));
} else if (code == 3) {
newErrors.append(tr("An invalid message was detected"));
} else if (code == 4) {
newErrors.append(tr("Not enough authorization for updating conversation's infos"));
} else {
continue;
}
newBackendErr.push_back(error);
}
set_backendErrors(newBackendErr);
set_errors(newErrors);
}
} catch (...) {
}
}

View file

@ -48,6 +48,8 @@ class CurrentConversation final : public QObject
QML_PROPERTY(bool, isContact)
QML_PROPERTY(bool, allMessagesLoaded)
QML_PROPERTY(QString, modeString)
QML_PROPERTY(QStringList, errors)
QML_PROPERTY(QStringList, backendErrors)
public:
explicit CurrentConversation(LRCInstance* lrcInstance, QObject* parent = nullptr);
@ -62,6 +64,7 @@ private Q_SLOTS:
void updateData();
void onConversationUpdated(const QString& convId);
void onProfileUpdated(const QString& convId);
void updateErrors(const QString& convId);
private:
LRCInstance* lrcInstance_;

View file

@ -21,6 +21,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import net.jami.Models 1.1
import net.jami.Adapters 1.1
@ -104,6 +105,14 @@ Rectangle {
}
}
ConversationErrorsRow {
id: errorRect
color: JamiTheme.filterBadgeColor
Layout.fillWidth: true
Layout.preferredHeight: JamiTheme.chatViewHeaderPreferredHeight
visible: false
}
SplitView {
id: chatViewMainRow
Layout.fillWidth: true

View file

@ -0,0 +1,102 @@
/*
* Copyright (C) 2022 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import "../../commoncomponents"
Rectangle {
id: root
opacity: visible
Connections {
target: CurrentConversation
enabled: true
onErrorsChanged: {
if (CurrentConversation.errors.length > 0) {
errorLabel.text = CurrentConversation.errors[0]
backendErrorToolTip.text = JamiStrings.backendError.arg(CurrentConversation.backendErrors[0])
}
errorRect.visible = CurrentConversation.errors.length > 0
}
}
RowLayout {
anchors.fill: parent
anchors.margins: JamiTheme.preferredMarginSize
Text {
id: errorLabel
Layout.alignment: Qt.AlignVCenter
text: CurrentConversation.errors.count > 0 ? CurrentConversation.errors[0][0] : ""
color: JamiTheme.filterBadgeTextColor
font.pixelSize: JamiTheme.headerFontSize
elide: Text.ElideRight
}
ResponsiveImage {
id: backEndError
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
width: 30
height: 30
source: JamiResources.outline_info_24dp_svg
layer {
enabled: true
effect: ColorOverlay {
color: JamiTheme.filterBadgeTextColor
}
}
MaterialToolTip {
id: backendErrorToolTip
text: ""
visible: parent.hovered && text !== ""
delay: Qt.styleHints.mousePressAndHoldInterval
}
}
PushButton {
id: btnClose
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
imageColor: JamiTheme.filterBadgeTextColor
normalColor: JamiTheme.transparentColor
source: JamiResources.round_close_24dp_svg
onClicked: ConversationsAdapter.popFrontError(CurrentConversation.id)
}
}
Behavior on opacity {
NumberAnimation {
from: 0
duration: JamiTheme.shortFadeDuration
}
}
}

View file

@ -77,6 +77,7 @@ struct Info
QString lastMessageUid = 0;
QHash<QString, QString> parentsId; // pair messageid/parentid for messages without parent loaded
unsigned int unreadMessages = 0;
QVector<QPair<int, QString>> errors;
QSet<QString> typers;

View file

@ -348,6 +348,11 @@ public:
* @param info
*/
void updateConversationInfos(const QString& conversationId, MapStringString info);
/**
* Remove first error
* @param conversationId
*/
void popFrontError(const QString& conversationId);
/**
* @return if conversations requests exists.
@ -427,6 +432,11 @@ Q_SIGNALS:
* @param uid
*/
void conversationUpdated(const QString& uid) const;
/**
* Emitted when a conversation detects an error
* @param uid
*/
void onConversationErrorsUpdated(const QString& uid) const;
/**
* Emitted when conversation's profile has been updated
* @param uid

View file

@ -340,6 +340,11 @@ CallbacksHandler::CallbacksHandler(const Lrc& parent)
this,
&CallbacksHandler::slotConversationMemberEvent,
Qt::QueuedConnection);
connect(&ConfigurationManager::instance(),
&ConfigurationManagerInterface::conversationError,
this,
&CallbacksHandler::slotOnConversationError,
Qt::QueuedConnection);
}
CallbacksHandler::~CallbacksHandler() {}
@ -791,4 +796,13 @@ CallbacksHandler::slotConversationMemberEvent(const QString& accountId,
Q_EMIT conversationMemberEvent(accountId, conversationId, memberId, event);
}
void
CallbacksHandler::slotOnConversationError(const QString& accountId,
const QString& conversationId,
int code,
const QString& what)
{
Q_EMIT conversationError(accountId, conversationId, code, what);
}
} // namespace lrc

View file

@ -366,6 +366,10 @@ Q_SIGNALS:
const QString& conversationId,
const QString& memberId,
int event);
void conversationError(const QString& accountId,
const QString& conversationId,
int code,
const QString& what);
private Q_SLOTS:
/**
@ -677,6 +681,10 @@ private Q_SLOTS:
const QString& conversationId,
const QString& memberId,
int event);
void slotOnConversationError(const QString& accountId,
const QString& conversationId,
int code,
const QString& what);
private:
const api::Lrc& parent;

View file

@ -369,6 +369,10 @@ public Q_SLOTS:
const QString& conversationId,
const QString& memberUri,
int event);
void slotOnConversationError(const QString& accountId,
const QString& conversationId,
int code,
const QString& what);
void slotConversationReady(const QString& accountId, const QString& conversationId);
void slotConversationRemoved(const QString& accountId, const QString& conversationId);
};
@ -988,6 +992,18 @@ ConversationModel::updateConversationInfos(const QString& conversationId, const
ConfigurationManager::instance().updateConversationInfos(owner.id, conversationId, newInfos);
}
void
ConversationModel::popFrontError(const QString& conversationId)
{
auto conversationOpt = getConversationForUid(conversationId);
if (!conversationOpt.has_value())
return;
auto& conversation = conversationOpt->get();
conversation.errors.pop_front();
Q_EMIT onConversationErrorsUpdated(conversationId);
}
bool
ConversationModel::hasPendingRequests() const
{
@ -1830,6 +1846,10 @@ ConversationModelPimpl::ConversationModelPimpl(const ConversationModel& linked,
&CallbacksHandler::conversationMemberEvent,
this,
&ConversationModelPimpl::slotConversationMemberEvent);
connect(&callbacksHandler,
&CallbacksHandler::conversationError,
this,
&ConversationModelPimpl::slotOnConversationError);
}
ConversationModelPimpl::~ConversationModelPimpl()
@ -1970,6 +1990,10 @@ ConversationModelPimpl::~ConversationModelPimpl()
&CallbacksHandler::conversationMemberEvent,
this,
&ConversationModelPimpl::slotConversationMemberEvent);
disconnect(&callbacksHandler,
&CallbacksHandler::conversationError,
this,
&ConversationModelPimpl::slotOnConversationError);
}
void
@ -2661,6 +2685,22 @@ ConversationModelPimpl::slotConversationMemberEvent(const QString& accountId,
Q_EMIT linked.dataChanged(indexOf(conversationId));
}
void
ConversationModelPimpl::slotOnConversationError(const QString& accountId,
const QString& conversationId,
int code,
const QString& what)
{
if (accountId != linked.owner.id || indexOf(conversationId) < 0) {
return;
}
try {
auto& conversation = getConversationForUid(conversationId).get();
conversation.errors.push_back({code, what});
Q_EMIT linked.onConversationErrorsUpdated(conversationId);
} catch (...) {}
}
void
ConversationModelPimpl::slotIncomingContactRequest(const QString& contactUri)
{

View file

@ -328,6 +328,16 @@ public:
QString(conversationId.c_str()),
QString(memberId.c_str()),
event);
}),
exportable_callback<ConversationSignal::OnConversationError>(
[this](const std::string& accountId,
const std::string& conversationId,
int code,
const std::string& what) {
Q_EMIT conversationError(QString(accountId.c_str()),
QString(conversationId.c_str()),
code,
QString(what.c_str()));
})};
}
@ -1190,6 +1200,10 @@ Q_SIGNALS: // SIGNALS
const QString& conversationId,
const QString& memberId,
int event);
void conversationError(const QString& accountId,
const QString& conversationId,
int code,
const QString& what);
};
namespace org {